2019-08-10 11:43:24 +00:00
// Hyperbolic Rogue -- basic computations in non-Euclidean geometry
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
/** \file hyperpoint.cpp
* \ brief basic computations in non - Euclidean geometry
*
* This implements hyperpoint ( a point in non - Euclidean space ) , transmatrix ( a transformation matrix ) ,
* and various basic routines related to them : rotations , translations , inverses and determinants , etc .
* For nonisotropic geometries , it rather refers to nonisotropic . cpp .
*/
2015-08-08 13:57:52 +00:00
2019-09-05 07:15:40 +00:00
# include "hyper.h"
2018-06-10 23:58:31 +00:00
namespace hr {
2019-08-09 19:18:13 +00:00
# if HDR
2022-11-12 21:38:45 +00:00
# ifndef M_PI
# define M_PI 3.14159265358979
# endif
static constexpr ld A_PI = M_PI ;
static const ld TAU = 2 * A_PI ;
static const ld degree = A_PI / 180 ;
2020-04-01 09:26:45 +00:00
static const ld golden_phi = ( sqrt ( 5 ) + 1 ) / 2 ;
static const ld log_golden_phi = log ( golden_phi ) ;
2022-11-12 21:38:45 +00:00
constexpr ld operator " " _deg ( long double deg ) { return deg * A_PI / 180 ; }
2019-08-09 19:18:13 +00:00
# endif
2018-08-28 15:17:34 +00:00
eGeometry geometry ;
eVariation variation ;
2019-02-21 17:47:32 +00:00
2016-08-26 09:58:03 +00:00
2019-08-09 23:56:00 +00:00
# if HDR
2019-08-22 11:45:51 +00:00
/** \brief A point in our continuous space
2020-03-27 20:47:09 +00:00
*
2019-08-22 11:45:51 +00:00
* Originally used for representing points in the hyperbolic plane .
* Currently used for all kinds of supported spaces , as well as
* for all vector spaces ( up to 4 dimensions ) . We are using
* the normalized homogeneous coordinates , which allows us to work with most
* geometries in HyperRogue in a uniform way .
* In the hyperbolic plane , this is the Minkowski hyperboloid model :
* ( x , y , z ) such that x * x + y * y - z * z = = - 1 and z > 0.
*
* In spherical geometry , we have x * x + y * y + z * z = = 1.
*
* In Euclidean geometry , we have z = 1.
*
* In isotropic 3 D geometries an extra coordinate is added .
*
* In nonisotropic coordinates h [ 3 ] = = 1.
*
* In product geometries the ' z ' coordinate is modelled by multiplying all
* three coordinates with exp ( z ) .
*
*/
2019-08-09 23:56:00 +00:00
struct hyperpoint : array < ld , MAXMDIM > {
hyperpoint ( ) { }
2019-09-13 17:23:22 +00:00
# if MAXMDIM == 4
constexpr hyperpoint ( ld x , ld y , ld z , ld w ) : array < ld , MAXMDIM > { { x , y , z , w } } { }
# else
constexpr hyperpoint ( ld x , ld y , ld z , ld w ) : array < ld , MAXMDIM > { { x , y , z } } { }
# endif
2019-08-09 23:56:00 +00:00
inline hyperpoint & operator * = ( ld d ) {
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) self [ i ] * = d ;
2019-08-09 23:56:00 +00:00
return self ;
}
inline hyperpoint & operator / = ( ld d ) {
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) self [ i ] / = d ;
2019-08-09 23:56:00 +00:00
return self ;
}
inline hyperpoint & operator + = ( const hyperpoint h2 ) {
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) self [ i ] + = h2 [ i ] ;
2019-08-09 23:56:00 +00:00
return self ;
}
inline hyperpoint & operator - = ( const hyperpoint h2 ) {
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) self [ i ] - = h2 [ i ] ;
2019-08-09 23:56:00 +00:00
return self ;
}
inline friend hyperpoint operator * ( ld d , hyperpoint h ) { return h * = d ; }
inline friend hyperpoint operator * ( hyperpoint h , ld d ) { return h * = d ; }
inline friend hyperpoint operator / ( hyperpoint h , ld d ) { return h / = d ; }
inline friend hyperpoint operator + ( hyperpoint h , hyperpoint h2 ) { return h + = h2 ; }
inline friend hyperpoint operator - ( hyperpoint h , hyperpoint h2 ) { return h - = h2 ; }
2019-08-24 16:14:38 +00:00
inline friend hyperpoint operator - ( hyperpoint h ) { return h * - 1 ; }
2019-08-09 23:56:00 +00:00
// cross product
inline friend hyperpoint operator ^ ( hyperpoint h1 , hyperpoint h2 ) {
return hyperpoint (
h1 [ 1 ] * h2 [ 2 ] - h1 [ 2 ] * h2 [ 1 ] ,
h1 [ 2 ] * h2 [ 0 ] - h1 [ 0 ] * h2 [ 2 ] ,
h1 [ 0 ] * h2 [ 1 ] - h1 [ 1 ] * h2 [ 0 ] ,
0
) ;
}
2021-05-17 12:22:19 +00:00
friend ld dot_d ( int c , hyperpoint h1 , hyperpoint h2 ) {
2019-08-09 23:56:00 +00:00
ld sum = 0 ;
2021-05-17 12:22:19 +00:00
for ( int i = 0 ; i < c ; i + + ) sum + = h1 [ i ] * h2 [ i ] ;
2019-08-09 23:56:00 +00:00
return sum ;
2021-05-17 12:22:19 +00:00
}
// Euclidean inner product
inline friend ld operator | ( hyperpoint h1 , hyperpoint h2 ) {
return dot_d ( MXDIM , h1 , h2 ) ;
2019-08-09 23:56:00 +00:00
}
} ;
2019-08-22 11:45:51 +00:00
/** \brief A matrix acting on hr::hyperpoint
2020-03-27 20:47:09 +00:00
*
2019-08-22 11:45:51 +00:00
* Since we are using homogeneous coordinates for hr : : hyperpoint ,
* rotations and translations can be represented
* as matrix multiplications . Other applications of matrices in HyperRogue
* ( in dimension up to 4 ) are also implemented using transmatrix .
*/
2019-08-09 23:56:00 +00:00
struct transmatrix {
ld tab [ MAXMDIM ] [ MAXMDIM ] ;
hyperpoint & operator [ ] ( int i ) { return ( hyperpoint & ) tab [ i ] [ 0 ] ; }
2020-05-15 20:53:09 +00:00
const hyperpoint & operator [ ] ( int i ) const { return ( const hyperpoint & ) tab [ i ] ; }
2019-08-09 23:56:00 +00:00
inline friend hyperpoint operator * ( const transmatrix & T , const hyperpoint & H ) {
hyperpoint z ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) {
2019-08-09 23:56:00 +00:00
z [ i ] = 0 ;
2020-11-01 16:37:51 +00:00
for ( int j = 0 ; j < MXDIM ; j + + ) z [ i ] + = T [ i ] [ j ] * H [ j ] ;
2019-08-09 23:56:00 +00:00
}
return z ;
}
inline friend transmatrix operator * ( const transmatrix & T , const transmatrix & U ) {
transmatrix R ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) for ( int j = 0 ; j < MXDIM ; j + + ) {
2019-08-09 23:56:00 +00:00
R [ i ] [ j ] = 0 ;
2020-11-01 16:37:51 +00:00
for ( int k = 0 ; k < MXDIM ; k + + )
2019-08-09 23:56:00 +00:00
R [ i ] [ j ] + = T [ i ] [ k ] * U [ k ] [ j ] ;
}
return R ;
}
} ;
2020-07-27 16:49:04 +00:00
/** @brief hyperpoint with shift
* shift has two uses :
* ( 1 ) in the ' universal cover of SL ' geometry , shift is used for the extra angular coordinate
* ( 2 ) in band models , shift is used to draw faraway points correctly
*/
struct shiftpoint {
hyperpoint h ;
ld shift ;
ld & operator [ ] ( int i ) { return h [ i ] ; }
const ld & operator [ ] ( int i ) const { return h [ i ] ; }
inline friend shiftpoint operator + ( const shiftpoint & h , const hyperpoint & h2 ) {
return shiftpoint { h . h + h2 , h . shift } ;
}
inline friend shiftpoint operator - ( const shiftpoint & h , const hyperpoint & h2 ) {
return shiftpoint { h . h - h2 , h . shift } ;
}
} ;
inline shiftpoint shiftless ( const hyperpoint & h , ld shift = 0 ) {
shiftpoint res ; res . h = h ; res . shift = shift ; return res ;
}
struct shiftmatrix {
transmatrix T ;
ld shift ;
hyperpoint & operator [ ] ( int i ) { return T [ i ] ; }
const hyperpoint & operator [ ] ( int i ) const { return T [ i ] ; }
inline friend shiftpoint operator * ( const shiftmatrix & T , const hyperpoint & h ) {
return shiftpoint { T . T * h , T . shift } ;
}
inline friend shiftmatrix operator * ( const shiftmatrix & T , const transmatrix & U ) {
return shiftmatrix { T . T * U , T . shift } ;
}
} ;
inline shiftmatrix shiftless ( const transmatrix & T , ld shift = 0 ) {
shiftmatrix res ; res . T = T ; res . shift = shift ; return res ;
}
2019-08-22 11:45:51 +00:00
/** returns a diagonal matrix */
2019-08-09 23:56:00 +00:00
constexpr transmatrix diag ( ld a , ld b , ld c , ld d ) {
# if MAXMDIM==3
return transmatrix { { { a , 0 , 0 } , { 0 , b , 0 } , { 0 , 0 , c } } } ;
# else
return transmatrix { { { a , 0 , 0 , 0 } , { 0 , b , 0 , 0 } , { 0 , 0 , c , 0 } , { 0 , 0 , 0 , d } } } ;
# endif
}
2019-08-24 10:58:44 +00:00
constexpr hyperpoint Hypc = hyperpoint ( 0 , 0 , 0 , 0 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** identity matrix */
2019-08-24 10:58:44 +00:00
constexpr transmatrix Id = diag ( 1 , 1 , 1 , 1 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** zero matrix */
2019-08-24 10:58:44 +00:00
constexpr transmatrix Zero = diag ( 0 , 0 , 0 , 0 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** mirror image */
2019-08-24 10:58:44 +00:00
constexpr transmatrix Mirror = diag ( 1 , - 1 , 1 , 1 ) ;
2019-08-22 11:45:51 +00:00
/** mirror image: flip in the Y coordinate */
2019-08-24 10:58:44 +00:00
constexpr transmatrix MirrorY = diag ( 1 , - 1 , 1 , 1 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** mirror image: flip in the X coordinate */
2019-08-24 10:58:44 +00:00
constexpr transmatrix MirrorX = diag ( - 1 , 1 , 1 , 1 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** mirror image: flip in the Z coordinate */
2019-08-24 10:58:44 +00:00
constexpr transmatrix MirrorZ = diag ( 1 , 1 , - 1 , 1 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** rotate by PI in the XY plane */
2019-08-24 10:58:44 +00:00
constexpr transmatrix pispin = diag ( - 1 , - 1 , 1 , 1 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** central symmetry matrix */
2019-08-24 10:58:44 +00:00
constexpr transmatrix centralsym = diag ( - 1 , - 1 , - 1 , - 1 ) ;
2019-08-09 23:56:00 +00:00
2019-08-18 11:05:47 +00:00
inline hyperpoint hpxyz ( ld x , ld y , ld z ) { return MDIM = = 3 ? hyperpoint ( x , y , z , 0 ) : hyperpoint ( x , y , 0 , z ) ; }
inline hyperpoint hpxyz3 ( ld x , ld y , ld z , ld w ) { return MDIM = = 3 ? hyperpoint ( x , y , w , 0 ) : hyperpoint ( x , y , z , w ) ; }
2019-08-24 10:58:44 +00:00
constexpr hyperpoint point3 ( ld x , ld y , ld z ) { return hyperpoint ( x , y , z , 0 ) ; }
2021-04-02 13:52:09 +00:00
constexpr hyperpoint point30 ( ld x , ld y , ld z ) { return hyperpoint ( x , y , z , 0 ) ; }
2019-08-24 10:58:44 +00:00
constexpr hyperpoint point31 ( ld x , ld y , ld z ) { return hyperpoint ( x , y , z , 1 ) ; }
constexpr hyperpoint point2 ( ld x , ld y ) { return hyperpoint ( x , y , 0 , 0 ) ; }
2019-08-09 23:56:00 +00:00
2019-08-24 10:58:44 +00:00
constexpr hyperpoint C02 = hyperpoint ( 0 , 0 , 1 , 0 ) ;
constexpr hyperpoint C03 = hyperpoint ( 0 , 0 , 0 , 1 ) ;
2019-08-09 23:56:00 +00:00
2019-08-22 11:45:51 +00:00
/** C0 is the origin in our space */
2019-08-17 21:28:41 +00:00
# define C0 (MDIM == 3 ? C02 : C03)
2019-08-09 23:56:00 +00:00
# endif
2015-08-08 13:57:52 +00:00
// basic functions and types
//===========================
2019-08-09 19:00:52 +00:00
EX ld squar ( ld x ) { return x * x ; }
2015-08-08 13:57:52 +00:00
2019-08-22 09:24:25 +00:00
EX int sig ( int z ) { return ginf [ geometry ] . g . sig [ z ] ; }
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX int curvature ( ) {
2018-03-27 02:01:30 +00:00
switch ( cgclass ) {
case gcEuclid : return 0 ;
case gcHyperbolic : return - 1 ;
case gcSphere : return 1 ;
2019-08-17 21:28:41 +00:00
case gcProduct : return PIU ( curvature ( ) ) ;
2018-03-27 02:01:30 +00:00
default : return 0 ;
}
}
2019-08-09 19:00:52 +00:00
EX ld sin_auto ( ld x ) {
2018-03-27 02:01:30 +00:00
switch ( cgclass ) {
case gcEuclid : return x ;
case gcHyperbolic : return sinh ( x ) ;
case gcSphere : return sin ( x ) ;
2019-08-17 21:28:41 +00:00
case gcProduct : return PIU ( sin_auto ( x ) ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return sinh ( x ) ;
2018-03-27 02:01:30 +00:00
default : return x ;
}
}
2019-08-09 19:00:52 +00:00
EX ld asin_auto ( ld x ) {
2018-03-27 02:01:30 +00:00
switch ( cgclass ) {
case gcEuclid : return x ;
case gcHyperbolic : return asinh ( x ) ;
case gcSphere : return asin ( x ) ;
2019-08-17 21:28:41 +00:00
case gcProduct : return PIU ( asin_auto ( x ) ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return asinh ( x ) ;
2018-03-27 02:01:30 +00:00
default : return x ;
}
}
2019-08-09 19:00:52 +00:00
EX ld acos_auto ( ld x ) {
2019-04-29 01:39:14 +00:00
switch ( cgclass ) {
case gcHyperbolic : return acosh ( x ) ;
case gcSphere : return acos ( x ) ;
2019-08-17 21:28:41 +00:00
case gcProduct : return PIU ( acos_auto ( x ) ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return acosh ( x ) ;
2019-04-29 01:39:14 +00:00
default : return x ;
}
}
2020-03-31 18:06:37 +00:00
/** \brief volume of a three-dimensional ball of radius r in the current isotropic geometry */
2019-08-09 19:00:52 +00:00
EX ld volume_auto ( ld r ) {
2019-03-30 16:45:56 +00:00
switch ( cgclass ) {
2022-11-12 21:38:45 +00:00
case gcEuclid : return r * r * r * 240. _deg ;
2019-03-30 16:45:56 +00:00
case gcHyperbolic : return M_PI * ( sinh ( 2 * r ) - 2 * r ) ;
case gcSphere : return M_PI * ( 2 * r - sin ( 2 * r ) ) ;
default : return 0 ;
}
}
2020-03-31 18:06:37 +00:00
/** \brief area of a circle of radius r in the current isotropic geometry */
EX ld area_auto ( ld r ) {
switch ( cgclass ) {
case gcEuclid : return r * r * M_PI ;
2022-11-12 21:38:45 +00:00
case gcHyperbolic : return TAU * ( cosh ( r ) - 1 ) ;
case gcSphere : return TAU * ( 1 - cos ( r ) ) ;
2020-03-31 18:06:37 +00:00
default : return 0 ;
}
}
/** \brief volume in 3D, area in 2D */
EX ld wvolarea_auto ( ld r ) {
if ( WDIM = = 3 ) return volume_auto ( r ) ;
else return area_auto ( r ) ;
}
2022-11-12 21:38:45 +00:00
EX ld asin_clamp ( ld x ) { return x > 1 ? 90. _deg : x < - 1 ? - 90. _deg : std : : isnan ( x ) ? 0 : asin ( x ) ; }
2019-08-26 07:06:39 +00:00
2021-04-07 12:46:10 +00:00
EX ld acos_clamp ( ld x ) { return x > 1 ? 0 : x < - 1 ? M_PI : std : : isnan ( x ) ? 0 : acos ( x ) ; }
2019-08-09 19:00:52 +00:00
EX ld asin_auto_clamp ( ld x ) {
2018-04-21 10:18:33 +00:00
switch ( cgclass ) {
case gcEuclid : return x ;
case gcHyperbolic : return asinh ( x ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return asinh ( x ) ;
2019-08-26 07:06:39 +00:00
case gcSphere : return asin_clamp ( x ) ;
2021-09-30 12:13:19 +00:00
case gcProduct : return PIU ( asin_auto_clamp ( x ) ) ;
2018-04-21 10:18:33 +00:00
default : return x ;
}
}
2019-08-18 14:58:44 +00:00
EX ld acos_auto_clamp ( ld x ) {
switch ( cgclass ) {
case gcHyperbolic : return x < 1 ? 0 : acosh ( x ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return x < 1 ? 0 : acosh ( x ) ;
2021-04-07 12:46:10 +00:00
case gcSphere : return acos_clamp ( x ) ;
2019-08-18 14:58:44 +00:00
case gcProduct : return PIU ( acos_auto_clamp ( x ) ) ;
default : return x ;
}
}
2019-08-09 19:00:52 +00:00
EX ld cos_auto ( ld x ) {
2018-03-27 02:01:30 +00:00
switch ( cgclass ) {
case gcEuclid : return 1 ;
case gcHyperbolic : return cosh ( x ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return cosh ( x ) ;
2018-03-27 02:01:30 +00:00
case gcSphere : return cos ( x ) ;
2019-08-17 21:28:41 +00:00
case gcProduct : return PIU ( cos_auto ( x ) ) ;
2018-03-27 02:01:30 +00:00
default : return 1 ;
}
}
2019-08-09 19:00:52 +00:00
EX ld tan_auto ( ld x ) {
2018-05-15 21:29:44 +00:00
switch ( cgclass ) {
case gcEuclid : return x ;
case gcHyperbolic : return tanh ( x ) ;
case gcSphere : return tan ( x ) ;
2019-08-17 21:28:41 +00:00
case gcProduct : return PIU ( tan_auto ( x ) ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return tanh ( x ) ;
2018-05-15 21:29:44 +00:00
default : return 1 ;
}
}
2019-08-09 19:00:52 +00:00
EX ld atan_auto ( ld x ) {
2018-05-15 21:29:44 +00:00
switch ( cgclass ) {
case gcEuclid : return x ;
case gcHyperbolic : return atanh ( x ) ;
case gcSphere : return atan ( x ) ;
2019-08-17 21:28:41 +00:00
case gcProduct : return PIU ( atan_auto ( x ) ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return atanh ( x ) ;
2019-07-23 13:08:07 +00:00
default : return x ;
2018-05-15 21:29:44 +00:00
}
}
2019-08-09 19:00:52 +00:00
EX ld atan2_auto ( ld y , ld x ) {
2018-05-15 21:29:44 +00:00
switch ( cgclass ) {
case gcEuclid : return y / x ;
case gcHyperbolic : return atanh ( y / x ) ;
2020-07-21 22:19:13 +00:00
case gcSL2 : return atanh ( y / x ) ;
2018-05-15 21:29:44 +00:00
case gcSphere : return atan2 ( y , x ) ;
2020-07-27 16:49:04 +00:00
case gcProduct : return PIU ( atan2_auto ( y , x ) ) ;
2019-07-23 13:08:07 +00:00
default : return y / x ;
2018-05-15 21:29:44 +00:00
}
}
2019-08-22 11:45:51 +00:00
/** This function returns the length of the edge opposite the angle alpha in
* a triangle with angles alpha , beta , gamma . This is called the cosine rule ,
* and of course works only in non - Euclidean geometry . */
2019-08-09 19:00:52 +00:00
EX ld edge_of_triangle_with_angles ( ld alpha , ld beta , ld gamma ) {
2019-04-29 01:39:14 +00:00
return acos_auto ( ( cos ( alpha ) + cos ( beta ) * cos ( gamma ) ) / ( sin ( beta ) * sin ( gamma ) ) ) ;
}
2019-09-05 10:00:55 +00:00
EX hyperpoint hpxy ( ld x , ld y ) {
2020-05-15 12:54:33 +00:00
if ( sl2 ) return hyperpoint ( x , y , 0 , sqrt ( 1 + x * x + y * y ) ) ;
if ( rotspace ) return hyperpoint ( x , y , 0 , sqrt ( 1 - x * x - y * y ) ) ;
2022-12-08 18:38:06 +00:00
if ( embedded_plane ) {
geom3 : : light_flip ( true ) ;
hyperpoint h = hpxy ( x , y ) ;
geom3 : : light_flip ( false ) ;
swapmatrix ( h ) ;
return h ;
}
2020-05-15 12:54:33 +00:00
return PIU ( hpxyz ( x , y , translatable ? 1 : sphere ? sqrt ( 1 - x * x - y * y ) : sqrt ( 1 + x * x + y * y ) ) ) ;
2019-02-22 19:58:40 +00:00
}
2019-09-05 10:00:55 +00:00
EX hyperpoint hpxy3 ( ld x , ld y , ld z ) {
2019-08-24 18:29:10 +00:00
return hpxyz3 ( x , y , z , sl2 ? sqrt ( 1 + x * x + y * y - z * z ) : translatable ? 1 : sphere ? sqrt ( 1 - x * x - y * y - z * z ) : sqrt ( 1 + x * x + y * y + z * z ) ) ;
2015-08-08 13:57:52 +00:00
}
2019-09-05 10:00:55 +00:00
# if HDR
2015-08-08 13:57:52 +00:00
// a point (I hope this number needs no comments ;) )
2019-08-24 10:58:44 +00:00
constexpr hyperpoint Cx12 = hyperpoint ( 1 , 0 , 1.41421356237 , 0 ) ;
constexpr hyperpoint Cx13 = hyperpoint ( 1 , 0 , 0 , 1.41421356237 ) ;
2019-02-25 13:50:39 +00:00
2019-05-08 16:33:08 +00:00
# define Cx1 (GDIM==2?Cx12:Cx13)
2019-09-05 10:00:55 +00:00
# endif
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX bool zero_d ( int d , hyperpoint h ) {
for ( int i = 0 ; i < d ; i + + ) if ( h [ i ] ) return false ;
return true ;
}
2019-08-22 11:45:51 +00:00
/** this function returns approximate square of distance between two points
* ( in the spherical analogy , this would be the distance in the 3 D space ,
* through the interior , not on the surface )
* also used to verify whether a point h1 is on the hyperbolic plane by using Hypc for h2
*/
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX ld intval ( const hyperpoint & h1 , const hyperpoint & h2 ) {
2019-02-17 17:47:19 +00:00
ld res = 0 ;
for ( int i = 0 ; i < MDIM ; i + + ) res + = squar ( h1 [ i ] - h2 [ i ] ) * sig ( i ) ;
2017-05-31 16:33:50 +00:00
if ( elliptic ) {
2019-02-17 17:47:19 +00:00
ld res2 = 0 ;
for ( int i = 0 ; i < MDIM ; i + + ) res2 + = squar ( h1 [ i ] + h2 [ i ] ) * sig ( i ) ;
return min ( res , res2 ) ;
2017-05-31 16:33:50 +00:00
}
2019-02-17 17:47:19 +00:00
return res ;
2017-03-23 10:53:57 +00:00
}
2019-08-17 21:28:41 +00:00
EX ld quickdist ( const hyperpoint & h1 , const hyperpoint & h2 ) {
2022-12-08 18:38:06 +00:00
if ( gproduct ) return hdist ( h1 , h2 ) ;
2019-08-17 21:28:41 +00:00
return intval ( h1 , h2 ) ;
}
2019-08-22 11:45:51 +00:00
/** square Euclidean hypotenuse in the first d dimensions */
2019-08-09 19:00:52 +00:00
EX ld sqhypot_d ( int d , const hyperpoint & h ) {
2019-02-22 19:58:40 +00:00
ld sum = 0 ;
for ( int i = 0 ; i < d ; i + + ) sum + = h [ i ] * h [ i ] ;
return sum ;
2017-12-27 05:31:47 +00:00
}
2019-08-22 11:45:51 +00:00
/** Euclidean hypotenuse in the first d dimensions */
2019-08-09 19:00:52 +00:00
EX ld hypot_d ( int d , const hyperpoint & h ) {
2019-02-27 18:33:13 +00:00
return sqrt ( sqhypot_d ( d , h ) ) ;
2017-12-27 09:52:54 +00:00
}
2020-05-15 09:43:13 +00:00
2020-05-15 16:31:32 +00:00
/** @brief h1 and h2 define a line; to_other_side(h1,h2)*x is x moved orthogonally to this line, by double the distance from C0
* ( I suppose it could be done better )
*/
EX transmatrix to_other_side ( hyperpoint h1 , hyperpoint h2 ) {
2022-12-15 20:17:08 +00:00
if ( geom3 : : sph_in_low ( ) & & ! geom3 : : flipped ) {
geom3 : : light_flip ( true ) ;
h1 = normalize ( h1 ) ;
h2 = normalize ( h2 ) ;
transmatrix T = to_other_side ( h1 , h2 ) ;
for ( int i = 0 ; i < 4 ; i + + ) T [ i ] [ 3 ] = T [ 3 ] [ i ] = i = = 3 ;
geom3 : : light_flip ( false ) ;
return T ;
}
2020-05-15 16:31:32 +00:00
ld d = hdist ( h1 , h2 ) ;
hyperpoint v ;
if ( euclid )
v = ( h2 - h1 ) / d ;
else
v = ( h1 * cos_auto ( d ) - h2 ) / sin_auto ( d ) ;
ld d1 ;
if ( euclid )
d1 = - ( v | h1 ) / ( v | v ) ;
else
d1 = atan_auto ( - v [ LDIM ] / h1 [ LDIM ] ) ;
2020-06-06 17:11:47 +00:00
hyperpoint hm = h1 * cos_auto ( d1 ) + ( sphere ? - 1 : 1 ) * v * sin_auto ( d1 ) ;
2020-05-15 16:31:32 +00:00
return rspintox ( hm ) * xpush ( - hdist0 ( hm ) * 2 ) * spintox ( hm ) ;
}
2020-05-15 09:43:13 +00:00
/** @brief positive for a material vertex, 0 for ideal vertex, negative for ultra-ideal vertex */
EX ld material ( const hyperpoint & h ) {
2022-02-26 08:46:40 +00:00
if ( sphere | | in_s2xe ( ) ) return intval ( h , Hypc ) ;
else if ( hyperbolic | | in_h2xe ( ) ) return - intval ( h , Hypc ) ;
2020-05-15 13:00:07 +00:00
else if ( sl2 ) return h [ 2 ] * h [ 2 ] + h [ 3 ] * h [ 3 ] - h [ 0 ] * h [ 0 ] - h [ 1 ] * h [ 1 ] ;
2020-05-25 21:53:31 +00:00
else return h [ LDIM ] ;
2020-05-15 09:43:13 +00:00
}
2022-04-25 17:53:36 +00:00
EX int safe_classify_ideals ( hyperpoint h ) {
if ( hyperbolic | | in_h2xe ( ) ) {
h / = h [ LDIM ] ;
ld x = MDIM = = 3 ? 1 - ( h [ 0 ] * h [ 0 ] + h [ 1 ] * h [ 1 ] ) : 1 - ( h [ 0 ] * h [ 0 ] + h [ 1 ] * h [ 1 ] + h [ 2 ] * h [ 2 ] ) ;
if ( x > 1e-6 ) return 1 ;
if ( x < - 1e-6 ) return - 1 ;
return 0 ;
}
return 1 ;
}
EX ld ideal_limit = 10 ;
EX ld ideal_each = degree ;
EX hyperpoint safe_approximation_of_ideal ( hyperpoint h ) {
return towards_inf ( C0 , h , ideal_limit ) ;
}
/** the point on the line ab which is closest to zero. Might not be normalized. Works even if a and b are (ultra)ideal */
EX hyperpoint closest_to_zero ( hyperpoint a , hyperpoint b ) {
if ( sqhypot_d ( MDIM , a - b ) < 1e-9 ) return a ;
if ( isnan ( a [ 0 ] ) ) return a ;
a / = a [ LDIM ] ;
b / = b [ LDIM ] ;
ld mul_a = 0 , mul_b = 0 ;
for ( int i = 0 ; i < LDIM ; i + + ) {
ld z = a [ i ] - b [ i ] ;
mul_a + = a [ i ] * z ;
mul_b - = b [ i ] * z ;
}
return ( mul_b * a + mul_a * b ) / ( mul_a + mul_b ) ;
}
2022-12-06 00:04:26 +00:00
/** should be called get_lof */
2019-08-09 19:00:52 +00:00
EX ld zlevel ( const hyperpoint & h ) {
2019-08-24 09:55:45 +00:00
if ( sl2 ) return sqrt ( - intval ( h , Hypc ) ) ;
else if ( translatable ) return h [ LDIM ] ;
2017-03-23 10:53:57 +00:00
else if ( sphere ) return sqrt ( intval ( h , Hypc ) ) ;
2019-11-28 22:30:29 +00:00
else if ( in_e2xe ( ) ) return log ( h [ 2 ] ) ;
2022-12-08 18:38:06 +00:00
else if ( gproduct ) return log ( sqrt ( abs ( intval ( h , Hypc ) ) ) ) ; /* abs works with both underlying spherical and hyperbolic */
2019-08-17 21:28:41 +00:00
else return ( h [ LDIM ] < 0 ? - 1 : 1 ) * sqrt ( - intval ( h , Hypc ) ) ;
2015-08-08 13:57:52 +00:00
}
2019-08-09 19:00:52 +00:00
EX ld hypot_auto ( ld x , ld y ) {
2018-03-27 02:01:30 +00:00
switch ( cgclass ) {
case gcEuclid :
return hypot ( x , y ) ;
case gcHyperbolic :
return acosh ( cosh ( x ) * cosh ( y ) ) ;
case gcSphere :
return acos ( cos ( x ) * cos ( y ) ) ;
default :
return hypot ( x , y ) ;
}
}
2019-08-22 11:45:51 +00:00
/** normalize the homogeneous coordinates */
2019-08-09 19:00:52 +00:00
EX hyperpoint normalize ( hyperpoint H ) {
2022-12-08 18:38:06 +00:00
if ( gproduct ) return H ;
2018-05-20 13:30:43 +00:00
ld Z = zlevel ( H ) ;
2020-11-01 16:37:51 +00:00
for ( int c = 0 ; c < MXDIM ; c + + ) H [ c ] / = Z ;
2018-04-03 23:19:21 +00:00
return H ;
}
2020-05-15 09:43:13 +00:00
/** like normalize but makes (ultra)ideal points material */
EX hyperpoint ultra_normalize ( hyperpoint H ) {
if ( material ( H ) < = 0 ) {
2022-02-26 08:51:07 +00:00
H [ LDIM ] = hypot_d ( LDIM , H ) + 1e-10 ;
2020-05-15 09:43:13 +00:00
}
return normalize ( H ) ;
}
2019-12-27 11:55:21 +00:00
/** normalize, and in product geometry, also flatten */
EX hyperpoint normalize_flat ( hyperpoint h ) {
2022-12-13 18:04:43 +00:00
if ( gproduct ) return product_decompose ( h ) . second ;
2019-12-27 11:55:21 +00:00
if ( sl2 ) h = slr : : translate ( h ) * zpush0 ( - atan2 ( h [ 2 ] , h [ 3 ] ) ) ;
2022-12-13 18:04:43 +00:00
if ( geom3 : : euc_in_nil ( ) ) h [ 1 ] = 0 ;
2022-12-13 22:26:44 +00:00
if ( geom3 : : euc_in_solnih ( ) ) h [ 2 ] = 0 ;
2022-12-15 10:43:26 +00:00
if ( geom3 : : hyp_in_solnih ( ) ) h [ 0 ] = 0 ;
2022-12-08 18:38:06 +00:00
if ( geom3 : : euc_in_hyp ( ) ) {
h = normalize ( h ) ;
auto h1 = deparabolic13 ( h ) ;
h1 [ 2 ] = 0 ;
return parabolic13 ( h1 ) ;
}
if ( geom3 : : sph_in_euc ( ) ) {
ld z = hypot_d ( 3 , h ) ;
if ( z > 0 ) h [ 0 ] / = z , h [ 1 ] / = z , h [ 2 ] / = z ;
h [ 3 ] = 1 ;
return h ;
}
if ( geom3 : : sph_in_hyp ( ) ) {
ld z = hypot_d ( 3 , h ) ;
z = sinh ( 1 ) / z ;
if ( z > 0 ) h [ 0 ] * = z , h [ 1 ] * = z , h [ 2 ] * = z ;
h [ 3 ] = cosh ( 1 ) ;
return h ;
}
2019-12-27 11:55:21 +00:00
return normalize ( h ) ;
}
2019-08-22 11:45:51 +00:00
/** get the center of the line segment from H1 to H2 */
2019-09-03 06:26:01 +00:00
EX hyperpoint mid ( const hyperpoint & H1 , const hyperpoint & H2 ) {
2022-12-08 18:38:06 +00:00
if ( gproduct ) {
2019-08-17 21:28:41 +00:00
auto d1 = product_decompose ( H1 ) ;
auto d2 = product_decompose ( H2 ) ;
2022-12-08 18:38:06 +00:00
hyperpoint res1 = PIU ( mid ( d1 . second , d2 . second ) ) ;
hyperpoint res = orthogonal_move ( res1 , ( d1 . first + d2 . first ) / 2 ) ;
return res ;
2019-08-17 21:28:41 +00:00
}
2018-04-03 23:19:21 +00:00
return normalize ( H1 + H2 ) ;
2015-08-08 13:57:52 +00:00
}
2020-07-27 16:49:04 +00:00
EX shiftpoint mid ( const shiftpoint & H1 , const shiftpoint & H2 ) {
return shiftless ( mid ( H1 . h , H2 . h ) , ( H1 . shift + H2 . shift ) / 2 ) ;
}
2019-08-22 11:45:51 +00:00
/** like mid, but take 3D into account */
2019-08-09 19:00:52 +00:00
EX hyperpoint midz ( const hyperpoint & H1 , const hyperpoint & H2 ) {
2022-12-08 18:38:06 +00:00
if ( gproduct ) return mid ( H1 , H2 ) ;
2018-04-03 23:19:21 +00:00
hyperpoint H3 = H1 + H2 ;
2016-08-26 09:58:03 +00:00
ld Z = 2 ;
2018-04-03 23:19:21 +00:00
if ( ! euclid ) Z = zlevel ( H3 ) * 2 / ( zlevel ( H1 ) + zlevel ( H2 ) ) ;
2020-11-01 16:37:51 +00:00
for ( int c = 0 ; c < MXDIM ; c + + ) H3 [ c ] / = Z ;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
return H3 ;
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
// matrices
//==========
2019-08-22 11:45:51 +00:00
/** rotate by alpha degrees in the coordinates a, b */
2019-08-09 19:00:52 +00:00
EX transmatrix cspin ( int a , int b , ld alpha ) {
2015-08-08 13:57:52 +00:00
transmatrix T = Id ;
2019-02-17 17:47:19 +00:00
T [ a ] [ a ] = + cos ( alpha ) ; T [ a ] [ b ] = + sin ( alpha ) ;
T [ b ] [ a ] = - sin ( alpha ) ; T [ b ] [ b ] = + cos ( alpha ) ;
2015-08-08 13:57:52 +00:00
return T ;
}
2022-11-12 21:38:45 +00:00
/** rotate by 90 degrees in the coordinates a, b */
EX transmatrix cspin90 ( int a , int b ) {
transmatrix T = Id ;
T [ a ] [ a ] = 0 ; T [ a ] [ b ] = 1 ;
T [ b ] [ a ] = - 1 ; T [ b ] [ b ] = 0 ;
return T ;
}
/** rotate by 180 degrees in the coordinates a, b */
EX transmatrix cspin180 ( int a , int b ) {
transmatrix T = Id ;
T [ a ] [ a ] = T [ b ] [ b ] = - 1 ;
return T ;
}
2019-08-22 11:45:51 +00:00
/** rotate by alpha degrees in the XY plane */
2022-12-13 18:04:43 +00:00
EX transmatrix spin ( ld alpha ) {
if ( embedded_plane & & geom3 : : euc_in_hyp ( ) & & ! destandarize_eih ) return cspin ( 1 , 2 , alpha ) ;
if ( embedded_plane & & geom3 : : euc_in_nil ( ) ) return cspin ( 0 , 2 , alpha ) ;
2022-12-15 10:43:26 +00:00
if ( embedded_plane & & geom3 : : hyp_in_solnih ( ) ) return cspin ( 1 , 2 , alpha ) ;
2022-12-13 18:04:43 +00:00
return cspin ( 0 , 1 , alpha ) ;
}
2019-02-17 17:47:19 +00:00
2022-12-17 16:37:26 +00:00
EX transmatrix logical_to_actual ( ) {
if ( embedded_plane & & geom3 : : euc_in_nil ( ) ) return cspin90 ( 2 , 1 ) ;
if ( embedded_plane & & geom3 : : hyp_in_solnih ( ) ) return cspin90 ( 0 , 1 ) * cspin90 ( 1 , 2 ) ;
return Id ;
}
EX transmatrix unswap_spin ( transmatrix T ) {
return ortho_inverse ( logical_to_actual ( ) ) * T * logical_to_actual ( ) ;
}
2022-11-12 21:38:45 +00:00
/** rotate by 90 degrees in the XY plane */
2022-12-13 18:04:43 +00:00
EX transmatrix spin90 ( ) {
if ( embedded_plane & & geom3 : : euc_in_hyp ( ) & & ! destandarize_eih ) return cspin90 ( 1 , 2 ) ;
if ( embedded_plane & & geom3 : : euc_in_nil ( ) ) return cspin90 ( 0 , 2 ) ;
2022-12-15 10:43:26 +00:00
if ( embedded_plane & & geom3 : : hyp_in_solnih ( ) ) return cspin90 ( 1 , 2 ) ;
2022-12-13 18:04:43 +00:00
return cspin90 ( 0 , 1 ) ;
}
2022-11-12 21:38:45 +00:00
/** rotate by 180 degrees in the XY plane */
2022-12-13 18:04:43 +00:00
EX transmatrix spin180 ( ) {
if ( embedded_plane & & geom3 : : euc_in_hyp ( ) & & ! destandarize_eih ) return cspin180 ( 1 , 2 ) ;
if ( embedded_plane & & geom3 : : euc_in_nil ( ) ) return cspin180 ( 0 , 2 ) ;
2022-12-15 10:43:26 +00:00
if ( embedded_plane & & geom3 : : hyp_in_solnih ( ) ) return cspin180 ( 1 , 2 ) ;
2022-12-13 18:04:43 +00:00
return cspin180 ( 0 , 1 ) ;
}
2022-11-12 21:38:45 +00:00
/** rotate by 270 degrees in the XY plane */
2022-12-13 18:04:43 +00:00
EX transmatrix spin270 ( ) {
if ( embedded_plane & & geom3 : : euc_in_hyp ( ) & & ! destandarize_eih ) return cspin90 ( 2 , 1 ) ;
if ( embedded_plane & & geom3 : : euc_in_nil ( ) ) return cspin90 ( 2 , 0 ) ;
2022-12-15 10:43:26 +00:00
if ( embedded_plane & & geom3 : : hyp_in_solnih ( ) ) return cspin90 ( 2 , 1 ) ;
2022-12-13 18:04:43 +00:00
return cspin90 ( 1 , 0 ) ;
}
2022-11-12 21:38:45 +00:00
2020-12-23 22:42:09 +00:00
EX transmatrix random_spin3 ( ) {
ld alpha2 = asin ( randd ( ) * 2 - 1 ) ;
2022-11-12 21:38:45 +00:00
ld alpha = randd ( ) * TAU ;
ld alpha3 = randd ( ) * TAU ;
2020-12-23 22:42:09 +00:00
return cspin ( 0 , 1 , alpha ) * cspin ( 0 , 2 , alpha2 ) * cspin ( 1 , 2 , alpha3 ) ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix random_spin ( ) {
2022-11-12 21:38:45 +00:00
if ( WDIM = = 2 ) return spin ( randd ( ) * TAU ) ;
2020-12-23 22:42:09 +00:00
else return random_spin3 ( ) ;
2019-03-30 16:51:37 +00:00
}
2019-08-09 19:00:52 +00:00
EX transmatrix eupush ( ld x , ld y ) {
2015-08-08 13:57:52 +00:00
transmatrix T = Id ;
2019-08-17 21:28:41 +00:00
T [ 0 ] [ LDIM ] = x ;
T [ 1 ] [ LDIM ] = y ;
2019-02-22 19:58:40 +00:00
return T ;
}
2020-12-26 21:59:58 +00:00
EX transmatrix euclidean_translate ( ld x , ld y , ld z ) {
transmatrix T = Id ;
T [ 0 ] [ LDIM ] = x ;
T [ 1 ] [ LDIM ] = y ;
T [ 2 ] [ LDIM ] = z ;
return T ;
}
EX transmatrix euscale ( ld x , ld y ) {
transmatrix T = Id ;
T [ 0 ] [ 0 ] = x ;
T [ 1 ] [ 1 ] = y ;
return T ;
}
EX transmatrix euscale3 ( ld x , ld y , ld z ) {
transmatrix T = Id ;
T [ 0 ] [ 0 ] = x ;
T [ 1 ] [ 1 ] = y ;
T [ 2 ] [ 2 ] = z ;
return T ;
}
2020-09-16 03:57:05 +00:00
EX transmatrix eupush ( hyperpoint h , ld co IS ( 1 ) ) {
if ( nonisotropic ) return nisot : : translate ( h , co ) ;
2017-12-14 01:53:29 +00:00
transmatrix T = Id ;
2020-09-16 03:57:05 +00:00
for ( int i = 0 ; i < GDIM ; i + + ) T [ i ] [ LDIM ] = h [ i ] * co ;
2017-12-14 01:53:29 +00:00
return T ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix eupush3 ( ld x , ld y , ld z ) {
2019-08-24 09:55:45 +00:00
if ( sl2 ) return slr : : translate ( slr : : xyz_point ( x , y , z ) ) ;
2019-08-06 10:00:46 +00:00
return eupush ( point3 ( x , y , z ) ) ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix euscalezoom ( hyperpoint h ) {
2017-12-14 01:53:29 +00:00
transmatrix T = Id ;
T [ 0 ] [ 0 ] = h [ 0 ] ;
T [ 0 ] [ 1 ] = - h [ 1 ] ;
T [ 1 ] [ 0 ] = h [ 1 ] ;
T [ 1 ] [ 1 ] = h [ 0 ] ;
return T ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix euaffine ( hyperpoint h ) {
2017-12-14 01:53:29 +00:00
transmatrix T = Id ;
2018-01-03 00:05:03 +00:00
T [ 0 ] [ 1 ] = h [ 0 ] ;
T [ 1 ] [ 1 ] = exp ( h [ 1 ] ) ;
2017-12-14 01:53:29 +00:00
return T ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix cpush ( int cid , ld alpha ) {
2022-12-08 18:38:06 +00:00
if ( gproduct & & cid = = 2 )
2022-12-06 00:04:26 +00:00
return scale_matrix ( Id , exp ( alpha ) ) ;
transmatrix T = Id ;
2019-08-06 19:04:40 +00:00
if ( nonisotropic )
2019-08-06 10:00:46 +00:00
return eupush3 ( cid = = 0 ? alpha : 0 , cid = = 1 ? alpha : 0 , cid = = 2 ? alpha : 0 ) ;
2019-08-17 21:28:41 +00:00
T [ LDIM ] [ LDIM ] = T [ cid ] [ cid ] = cos_auto ( alpha ) ;
T [ cid ] [ LDIM ] = sin_auto ( alpha ) ;
T [ LDIM ] [ cid ] = - curvature ( ) * sin_auto ( alpha ) ;
2015-08-08 13:57:52 +00:00
return T ;
}
2022-12-06 00:04:26 +00:00
EX transmatrix lzpush ( ld z ) {
2022-12-08 18:38:06 +00:00
if ( geom3 : : euc_in_hyp ( ) & & ! destandarize_eih ) return cpush ( 0 , z ) ;
2022-12-15 10:43:26 +00:00
if ( geom3 : : hyp_in_solnih ( ) ) return cpush ( 0 , z ) ;
2022-12-13 18:04:43 +00:00
if ( geom3 : : euc_in_nil ( ) ) return cpush ( 1 , z ) ;
2022-12-06 00:04:26 +00:00
return cpush ( 2 , z ) ;
}
2021-07-07 16:21:17 +00:00
EX transmatrix cmirror ( int cid ) {
transmatrix T = Id ;
T [ cid ] [ cid ] = - 1 ;
return T ;
}
2019-02-17 17:47:19 +00:00
// push alpha units to the right
2019-08-09 19:00:52 +00:00
EX transmatrix xpush ( ld alpha ) { return cpush ( 0 , alpha ) ; }
2017-03-23 10:53:57 +00:00
2022-12-08 18:38:06 +00:00
EX transmatrix lxpush ( ld alpha ) {
2022-12-08 21:06:44 +00:00
if ( embedded_plane ) {
2022-12-08 18:38:06 +00:00
geom3 : : light_flip ( true ) ;
auto t = cpush ( 0 , alpha ) ;
geom3 : : light_flip ( false ) ;
swapmatrix ( t ) ;
return t ;
}
return cpush ( 0 , alpha ) ;
}
2019-08-09 19:00:52 +00:00
EX bool eqmatrix ( transmatrix A , transmatrix B , ld eps IS ( .01 ) ) {
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + )
for ( int j = 0 ; j < MXDIM ; j + + )
2019-05-04 16:29:52 +00:00
if ( std : : abs ( A [ i ] [ j ] - B [ i ] [ j ] ) > eps )
2019-03-15 11:45:57 +00:00
return false ;
return true ;
}
2019-05-11 17:43:46 +00:00
# if MAXMDIM >= 4
2022-12-06 00:04:26 +00:00
/** in the 3D space, move the point h orthogonally to the (x,y) plane by z units */
2019-08-09 19:00:52 +00:00
EX hyperpoint orthogonal_move ( const hyperpoint & h , ld z ) {
2022-12-08 18:38:06 +00:00
if ( geom3 : : euc_in_hyp ( ) ) {
hyperpoint hf = deparabolic13 ( h ) ;
hf [ 2 ] + = z ;
return parabolic13 ( hf ) ;
}
2022-12-13 18:04:43 +00:00
if ( geom3 : : euc_in_nil ( ) ) {
return nisot : : translate ( h ) * cpush0 ( 1 , z ) ;
}
2022-12-13 22:26:44 +00:00
if ( geom3 : : euc_in_solnih ( ) ) {
return nisot : : translate ( h ) * cpush0 ( 2 , z ) ;
}
2022-12-08 18:38:06 +00:00
if ( geom3 : : sph_in_euc ( ) ) {
ld z0 = hypot_d ( 3 , h ) ;
ld f = ( ( z0 + z ) / z0 ) ;
hyperpoint hf ;
for ( int i = 0 ; i < 3 ; i + + ) hf [ i ] = h [ i ] * f ;
hf [ 3 ] = 1 ;
return hf ;
}
2022-12-15 10:43:26 +00:00
if ( geom3 : : hyp_in_solnih ( ) ) {
return nisot : : translate ( h ) * cpush0 ( 0 , z ) ;
}
2022-12-08 18:38:06 +00:00
if ( geom3 : : sph_in_hyp ( ) ) {
ld z0 = acosh ( h [ 3 ] ) ;
ld f = sinh ( z0 + z ) / sinh ( z0 ) ;
hyperpoint hf ;
for ( int i = 0 ; i < 3 ; i + + ) hf [ i ] = h [ i ] * f ;
hf [ 3 ] = cosh ( z0 + z ) ;
return hf ;
}
2022-12-06 00:04:26 +00:00
if ( GDIM = = 2 ) return scale_point ( h , geom3 : : scale_at_lev ( z ) ) ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) return scale_point ( h , exp ( z ) ) ;
2019-08-24 12:49:49 +00:00
if ( sl2 ) return slr : : translate ( h ) * cpush0 ( 2 , z ) ;
2019-05-15 07:34:40 +00:00
if ( ! hyperbolic ) return rgpushxto0 ( h ) * cpush ( 2 , z ) * C0 ;
2019-08-06 10:00:46 +00:00
if ( nil ) return nisot : : translate ( h ) * cpush0 ( 2 , z ) ;
if ( translatable ) return hpxy3 ( h [ 0 ] , h [ 1 ] , h [ 2 ] + z ) ;
2019-05-11 17:43:46 +00:00
ld u = 1 ;
2020-06-03 14:11:59 +00:00
if ( h [ 2 ] ) z + = asin_auto ( h [ 2 ] ) , u / = cos_auto ( asin_auto ( h [ 2 ] ) ) ;
2019-05-15 07:34:40 +00:00
u * = cos_auto ( z ) ;
2019-05-11 17:43:46 +00:00
return hpxy3 ( h [ 0 ] * u , h [ 1 ] * u , sinh ( z ) ) ;
}
# endif
2015-08-08 13:57:52 +00:00
// push alpha units vertically
2019-08-09 19:00:52 +00:00
EX transmatrix ypush ( ld alpha ) { return cpush ( 1 , alpha ) ; }
2019-02-17 17:47:19 +00:00
2019-08-09 19:00:52 +00:00
EX transmatrix zpush ( ld z ) { return cpush ( 2 , z ) ; }
2019-07-23 13:06:03 +00:00
2019-08-09 19:00:52 +00:00
EX transmatrix matrix3 ( ld a , ld b , ld c , ld d , ld e , ld f , ld g , ld h , ld i ) {
2019-08-07 18:03:50 +00:00
# if MAXMDIM==3
return transmatrix { { { a , b , c } , { d , e , f } , { g , h , i } } } ;
# else
2019-08-15 13:05:43 +00:00
if ( GDIM = = 2 )
2019-08-07 18:03:50 +00:00
return transmatrix { { { a , b , c , 0 } , { d , e , f , 0 } , { g , h , i , 0 } , { 0 , 0 , 0 , 1 } } } ;
2019-05-08 18:15:50 +00:00
else
2019-08-07 18:03:50 +00:00
return transmatrix { { { a , b , 0 , c } , { d , e , 0 , f } , { 0 , 0 , 1 , 0 } , { g , h , 0 , i } } } ;
# endif
2019-02-22 19:58:40 +00:00
}
2019-08-09 19:00:52 +00:00
EX transmatrix matrix4 ( ld a , ld b , ld c , ld d , ld e , ld f , ld g , ld h , ld i , ld j , ld k , ld l , ld m , ld n , ld o , ld p ) {
2019-08-07 18:03:50 +00:00
# if MAXMDIM==3
return transmatrix { { { a , b , d } , { e , f , h } , { m , n , p } } } ;
# else
return transmatrix { { { a , b , c , d } , { e , f , g , h } , { i , j , k , l } , { m , n , o , p } } } ;
# endif
2019-02-22 19:58:40 +00:00
}
2019-05-08 19:09:22 +00:00
# if MAXMDIM >= 4
2022-12-08 18:38:06 +00:00
/** Transform a matrix between the 'embedded_plane' and underlying representation. Switches to the current variant. */
2019-08-09 19:00:52 +00:00
EX void swapmatrix ( transmatrix & T ) {
2022-12-08 18:38:06 +00:00
if ( geom3 : : euc_in_hyp ( ) & & ! geom3 : : flipped ) {
geom3 : : light_flip ( true ) ;
hyperpoint mov = T * C02 ;
transmatrix U = gpushxto0 ( mov ) * T ;
geom3 : : light_flip ( false ) ;
for ( int i = 0 ; i < 4 ; i + + ) U [ i ] [ 3 ] = U [ 3 ] [ i ] = i = = 3 ;
T = parabolic13 ( mov [ 0 ] , mov [ 1 ] ) * U ;
}
2022-12-15 10:43:26 +00:00
else if ( geom3 : : hyp_in_solnih ( ) ) {
// rotations are illegal anyway...
hyperpoint h = get_column ( T , 2 ) ;
swapmatrix ( h ) ;
T = rgpushxto0 ( h ) ;
return ;
}
2022-12-08 18:38:06 +00:00
else if ( geom3 : : sph_in_euc ( ) | | geom3 : : sph_in_hyp ( ) ) {
if ( ! geom3 : : flipped ) {
for ( int i = 0 ; i < 4 ; i + + ) T [ i ] [ 3 ] = T [ 3 ] [ i ] = i = = 3 ;
}
}
2022-12-13 18:04:43 +00:00
else if ( geom3 : : euc_in_nil ( ) ) {
if ( ! geom3 : : flipped ) {
2022-12-15 10:43:43 +00:00
hyperpoint h1 = get_column ( T , 2 ) ;
2022-12-13 18:04:43 +00:00
// rotations are illegal anyway...
T = eupush ( hyperpoint ( h1 [ 0 ] * geom3 : : euclid_embed_scale , 0 , h1 [ 1 ] * geom3 : : euclid_embed_scale , 1 ) ) ;
2022-12-15 10:43:43 +00:00
return ;
2022-12-13 18:04:43 +00:00
}
}
2022-12-13 22:26:44 +00:00
else if ( geom3 : : euc_in_solnih ( ) ) {
if ( ! geom3 : : flipped ) {
2022-12-15 10:43:43 +00:00
hyperpoint h1 = get_column ( T , 2 ) ;
2022-12-13 22:26:44 +00:00
// rotations are illegal anyway...
T = eupush ( hyperpoint ( h1 [ 0 ] * geom3 : : euclid_embed_scale , h1 [ 1 ] * geom3 : : euclid_embed_scale , 0 , 1 ) ) ;
2022-12-15 10:43:43 +00:00
return ;
2022-12-13 22:26:44 +00:00
}
}
2022-12-08 18:38:06 +00:00
else if ( geom3 : : in_product ( ) ) {
/* just do nothing */
}
else {
for ( int i = 0 ; i < 4 ; i + + ) swap ( T [ i ] [ 2 ] , T [ i ] [ 3 ] ) ;
for ( int i = 0 ; i < 4 ; i + + ) swap ( T [ 2 ] [ i ] , T [ 3 ] [ i ] ) ;
if ( GDIM = = 3 ) {
for ( int i = 0 ; i < 4 ; i + + ) T [ i ] [ 2 ] = T [ 2 ] [ i ] = 0 ;
T [ 2 ] [ 2 ] = 1 ;
}
2019-05-08 19:09:22 +00:00
}
2019-05-10 02:03:07 +00:00
fixmatrix ( T ) ;
2022-12-08 18:38:06 +00:00
for ( int i = 0 ; i < MDIM ; i + + ) for ( int j = 0 ; j < MDIM ; j + + ) if ( isnan ( T [ i ] [ j ] ) ) T = Id ;
2019-05-08 19:09:22 +00:00
}
2019-05-15 13:20:37 +00:00
2022-12-08 18:38:06 +00:00
/** Just like swapmatrix but for hyperpoints. */
2019-08-09 19:00:52 +00:00
EX void swapmatrix ( hyperpoint & h ) {
2022-12-08 18:38:06 +00:00
if ( geom3 : : in_product ( ) ) return ;
if ( geom3 : : sph_in_euc ( ) ) { h [ 3 ] = 1 ; return ; }
if ( geom3 : : sph_in_hyp ( ) ) { h [ 0 ] * = sinh ( 1 ) ; h [ 1 ] * = sinh ( 1 ) ; h [ 2 ] * = sinh ( 1 ) ; h [ 3 ] = cosh ( 1 ) ; return ; }
2022-12-13 18:04:43 +00:00
if ( geom3 : : euc_in_nil ( ) ) { h [ 3 ] = 1 ; h [ 2 ] = h [ 1 ] * geom3 : : euclid_embed_scale ; h [ 1 ] = 0 ; h [ 0 ] * = geom3 : : euclid_embed_scale ; return ; }
2022-12-13 22:26:44 +00:00
if ( geom3 : : euc_in_solnih ( ) ) { h [ 3 ] = 1 ; h [ 1 ] = h [ 1 ] * geom3 : : euclid_embed_scale ; h [ 2 ] = 0 ; h [ 0 ] * = geom3 : : euclid_embed_scale ; return ; }
2022-12-15 10:43:26 +00:00
if ( geom3 : : hyp_in_solnih ( ) ) {
// copied from deparabolic13
h / = ( 1 + h [ 2 ] ) ;
h [ 0 ] - = 1 ;
h / = sqhypot_d ( 2 , h ) ;
h [ 0 ] + = .5 ;
ld hx = log ( 2 ) + log ( - h [ 0 ] ) ;
if ( cgclass = = gcNIH ) hx / = log ( 3 ) ;
if ( cgclass = = gcSolN ) hx / = log ( 3 ) ;
ld hy = h [ 1 ] * 2 ;
h = point31 ( 0 , hy , hx ) ;
return ;
}
2019-05-15 13:20:37 +00:00
swap ( h [ 2 ] , h [ 3 ] ) ;
2022-12-08 18:38:06 +00:00
if ( GDIM = = 3 ) h [ 2 ] = 0 ;
if ( geom3 : : euc_in_hyp ( ) ) h = parabolic13 ( h [ 0 ] , h [ 1 ] ) * C0 ;
2019-05-15 13:20:37 +00:00
}
2019-05-08 19:09:22 +00:00
# endif
2019-08-09 19:00:52 +00:00
EX transmatrix parabolic1 ( ld u ) {
2019-02-17 17:47:19 +00:00
if ( euclid )
return ypush ( u ) ;
2022-12-15 10:43:26 +00:00
else if ( geom3 : : hyp_in_solnih ( ) ) {
return ypush ( u ) ;
}
2017-03-23 10:53:57 +00:00
else {
2019-02-22 19:58:40 +00:00
ld diag = u * u / 2 ;
return matrix3 (
- diag + 1 , u , diag ,
- u , 1 , u ,
- diag , u , diag + 1
) ;
}
}
2022-12-08 18:38:06 +00:00
EX bool destandarize_eih = true ;
2019-08-09 19:00:52 +00:00
EX transmatrix parabolic13 ( ld u , ld v ) {
2019-02-22 19:58:40 +00:00
if ( euclid )
2021-10-07 11:23:44 +00:00
return eupush3 ( 0 , u , v ) ;
2022-12-08 18:38:06 +00:00
else if ( geom3 : : euc_in_hyp ( ) & & destandarize_eih ) {
ld diag = ( u * u + v * v ) / 2 ;
return matrix4 (
1 , 0 , - u , u ,
0 , 1 , - v , v ,
u , v , - diag + 1 , diag ,
u , v , - diag , diag + 1
) ;
}
2019-02-22 19:58:40 +00:00
else {
ld diag = ( u * u + v * v ) / 2 ;
return matrix4 (
- diag + 1 , u , v , diag ,
- u , 1 , 0 , u ,
- v , 0 , 1 , v ,
- diag , u , v , diag + 1
) ;
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
}
2021-10-07 11:23:44 +00:00
EX hyperpoint deparabolic13 ( hyperpoint h ) {
2019-11-02 09:40:22 +00:00
if ( euclid ) return h ;
2022-12-08 18:38:06 +00:00
if ( geom3 : : euc_in_hyp ( ) & & destandarize_eih ) {
h / = ( 1 + h [ LDIM ] ) ;
h [ 2 ] - = 1 ;
h / = sqhypot_d ( LDIM , h ) ;
h [ 2 ] + = .5 ;
return point3 ( h [ 0 ] * 2 , h [ 1 ] * 2 , log ( 2 ) + log ( - h [ 2 ] ) ) ;
}
2021-10-07 11:23:44 +00:00
h / = ( 1 + h [ LDIM ] ) ;
h [ 0 ] - = 1 ;
h / = sqhypot_d ( LDIM , h ) ;
h [ 0 ] + = .5 ;
return point3 ( log ( 2 ) + log ( - h [ 0 ] ) , h [ 1 ] * 2 , LDIM = = 3 ? h [ 2 ] * 2 : 0 ) ;
2019-11-02 09:40:22 +00:00
}
2021-10-07 11:23:44 +00:00
EX hyperpoint parabolic13 ( hyperpoint h ) {
if ( euclid ) return h ;
2022-12-08 18:38:06 +00:00
else if ( geom3 : : euc_in_hyp ( ) & & destandarize_eih ) {
return parabolic13 ( h [ 0 ] , h [ 1 ] ) * cpush0 ( 2 , h [ 2 ] ) ;
}
2021-10-07 11:23:44 +00:00
else if ( LDIM = = 3 )
return parabolic13 ( h [ 1 ] , h [ 2 ] ) * xpush0 ( h [ 0 ] ) ;
else
return parabolic1 ( h [ 1 ] ) * xpush0 ( h [ 0 ] ) ;
2021-10-07 10:41:29 +00:00
}
2022-12-08 18:38:06 +00:00
EX transmatrix parabolic13_at ( hyperpoint h ) {
if ( euclid ) return rgpushxto0 ( h ) ;
else if ( geom3 : : euc_in_hyp ( ) & & destandarize_eih ) {
return parabolic13 ( h [ 0 ] , h [ 1 ] ) * cpush ( 2 , h [ 2 ] ) ;
}
else if ( LDIM = = 3 )
return parabolic13 ( h [ 1 ] , h [ 2 ] ) * xpush ( h [ 0 ] ) ;
else
return parabolic1 ( h [ 1 ] ) * xpush ( h [ 0 ] ) ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix spintoc ( const hyperpoint & H , int t , int f ) {
2019-02-17 17:47:19 +00:00
transmatrix T = Id ;
ld R = hypot ( H [ f ] , H [ t ] ) ;
2022-02-26 08:51:07 +00:00
if ( R > = 1e-15 ) {
2019-02-17 17:47:19 +00:00
T [ t ] [ t ] = + H [ t ] / R ; T [ t ] [ f ] = + H [ f ] / R ;
T [ f ] [ t ] = - H [ f ] / R ; T [ f ] [ f ] = + H [ t ] / R ;
}
return T ;
2018-11-25 22:36:50 +00:00
}
2019-08-22 11:45:51 +00:00
/** an Euclidean rotation in the axes (t,f) which rotates
* the point H to the positive ' t ' axis
*/
2019-08-09 19:00:52 +00:00
EX transmatrix rspintoc ( const hyperpoint & H , int t , int f ) {
2015-08-08 13:57:52 +00:00
transmatrix T = Id ;
2019-02-17 17:47:19 +00:00
ld R = hypot ( H [ f ] , H [ t ] ) ;
2022-02-26 08:51:07 +00:00
if ( R > = 1e-15 ) {
2019-02-17 17:47:19 +00:00
T [ t ] [ t ] = + H [ t ] / R ; T [ t ] [ f ] = - H [ f ] / R ;
T [ f ] [ t ] = + H [ f ] / R ; T [ f ] [ f ] = + H [ t ] / R ;
2015-08-08 13:57:52 +00:00
}
return T ;
}
2019-08-22 11:45:51 +00:00
/** an isometry which takes the point H to the positive X axis
* \ see rspintox
*/
2019-08-09 19:00:52 +00:00
EX transmatrix spintox ( const hyperpoint & H ) {
2022-12-08 18:38:06 +00:00
if ( GDIM = = 2 | | gproduct ) return spintoc ( H , 0 , 1 ) ;
2019-02-17 17:47:19 +00:00
transmatrix T1 = spintoc ( H , 0 , 1 ) ;
return spintoc ( T1 * H , 0 , 2 ) * T1 ;
}
2019-08-22 11:45:51 +00:00
/** inverse of hr::spintox
*/
EX transmatrix rspintox ( const hyperpoint & H ) {
2022-12-08 18:38:06 +00:00
if ( GDIM = = 2 | | gproduct ) return rspintoc ( H , 0 , 1 ) ;
transmatrix T1 = spintoc ( H , 0 , 1 ) ;
return rspintoc ( H , 0 , 1 ) * rspintoc ( T1 * H , 0 , 2 ) ;
}
EX transmatrix lspintox ( const hyperpoint & H ) {
2022-12-13 18:04:43 +00:00
if ( geom3 : : euc_in_nil ( ) ) return spintoc ( H , 0 , 2 ) ;
2022-12-15 10:43:26 +00:00
if ( geom3 : : hyp_in_solnih ( ) ) return spintoc ( H , 1 , 2 ) ;
2022-12-08 18:38:06 +00:00
if ( WDIM = = 2 | | gproduct ) return spintoc ( H , 0 , 1 ) ;
transmatrix T1 = spintoc ( H , 0 , 1 ) ;
return spintoc ( T1 * H , 0 , 2 ) * T1 ;
}
EX transmatrix lrspintox ( const hyperpoint & H ) {
2022-12-13 18:04:43 +00:00
if ( geom3 : : euc_in_nil ( ) ) return rspintoc ( H , 0 , 2 ) ;
2022-12-15 10:43:26 +00:00
if ( geom3 : : hyp_in_solnih ( ) ) return rspintoc ( H , 1 , 2 ) ;
2022-12-08 18:38:06 +00:00
if ( WDIM = = 2 | | gproduct ) return rspintoc ( H , 0 , 1 ) ;
2019-08-22 11:45:51 +00:00
transmatrix T1 = spintoc ( H , 0 , 1 ) ;
return rspintoc ( H , 0 , 1 ) * rspintoc ( T1 * H , 0 , 2 ) ;
}
/** for H on the X axis, this matrix pushes H to C0
* \ see gpushxto0
*/
EX transmatrix pushxto0 ( const hyperpoint & H ) {
transmatrix T = Id ;
T [ 0 ] [ 0 ] = + H [ LDIM ] ; T [ 0 ] [ LDIM ] = - H [ 0 ] ;
T [ LDIM ] [ 0 ] = curvature ( ) * H [ 0 ] ; T [ LDIM ] [ LDIM ] = + H [ LDIM ] ;
return T ;
}
/** set the i-th column of T to H */
2019-08-09 19:00:52 +00:00
EX void set_column ( transmatrix & T , int i , const hyperpoint & H ) {
2020-11-01 16:37:51 +00:00
for ( int j = 0 ; j < MXDIM ; j + + )
2018-04-03 21:39:18 +00:00
T [ j ] [ i ] = H [ j ] ;
}
2021-05-29 13:46:35 +00:00
EX hyperpoint get_column ( transmatrix & T , int i ) {
hyperpoint h ;
for ( int j = 0 ; j < MXDIM ; j + + )
h [ j ] = T [ j ] [ i ] ;
return h ;
}
2019-08-22 11:45:51 +00:00
/** build a matrix using the given vectors as columns */
2019-08-09 19:00:52 +00:00
EX transmatrix build_matrix ( hyperpoint h1 , hyperpoint h2 , hyperpoint h3 , hyperpoint h4 ) {
2019-05-11 13:13:55 +00:00
transmatrix T ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) {
2018-04-03 23:19:21 +00:00
T [ i ] [ 0 ] = h1 [ i ] ,
T [ i ] [ 1 ] = h2 [ i ] ,
2019-02-22 19:58:40 +00:00
T [ i ] [ 2 ] = h3 [ i ] ;
2020-11-01 16:37:51 +00:00
if ( MAXMDIM = = 4 ) T [ i ] [ 3 ] = h4 [ i ] ;
}
2018-04-03 23:19:21 +00:00
return T ;
}
2019-08-22 11:45:51 +00:00
/** for H on the X axis, this matrix pushes C0 to H
* \ see rgpushxto0
*/
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX transmatrix rpushxto0 ( const hyperpoint & H ) {
2015-08-08 13:57:52 +00:00
transmatrix T = Id ;
2019-08-17 21:28:41 +00:00
T [ 0 ] [ 0 ] = + H [ LDIM ] ; T [ 0 ] [ LDIM ] = H [ 0 ] ;
T [ LDIM ] [ 0 ] = - curvature ( ) * H [ 0 ] ; T [ LDIM ] [ LDIM ] = + H [ LDIM ] ;
2015-08-08 13:57:52 +00:00
return T ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix ggpushxto0 ( const hyperpoint & H , ld co ) {
2020-09-16 03:57:05 +00:00
if ( translatable )
return eupush ( H , co ) ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) {
2019-08-17 21:28:41 +00:00
auto d = product_decompose ( H ) ;
2022-12-06 00:04:26 +00:00
return scale_matrix ( PIU ( ggpushxto0 ( d . second , co ) ) , exp ( d . first * co ) ) ;
2019-08-17 21:28:41 +00:00
}
2019-02-17 17:47:19 +00:00
transmatrix res = Id ;
2022-02-26 08:51:07 +00:00
if ( sqhypot_d ( GDIM , H ) < 1e-16 ) return res ;
2020-09-22 13:51:37 +00:00
ld fac = - curvature ( ) / ( H [ LDIM ] + 1 ) ;
2019-05-08 16:33:08 +00:00
for ( int i = 0 ; i < GDIM ; i + + )
for ( int j = 0 ; j < GDIM ; j + + )
2019-02-17 17:47:19 +00:00
res [ i ] [ j ] + = H [ i ] * H [ j ] * fac ;
2019-05-08 16:33:08 +00:00
for ( int d = 0 ; d < GDIM ; d + + )
2019-08-17 21:28:41 +00:00
res [ d ] [ LDIM ] = co * H [ d ] ,
res [ LDIM ] [ d ] = - curvature ( ) * co * H [ d ] ;
res [ LDIM ] [ LDIM ] = H [ LDIM ] ;
2019-02-17 17:47:19 +00:00
return res ;
}
2019-08-22 11:45:51 +00:00
/** a translation matrix which takes H to 0 */
2019-08-09 19:00:52 +00:00
EX transmatrix gpushxto0 ( const hyperpoint & H ) {
2019-02-17 17:47:19 +00:00
return ggpushxto0 ( H , - 1 ) ;
2015-08-08 13:57:52 +00:00
}
2019-08-22 11:45:51 +00:00
/** a translation matrix which takes 0 to H */
2019-08-09 19:00:52 +00:00
EX transmatrix rgpushxto0 ( const hyperpoint & H ) {
2019-02-17 17:47:19 +00:00
return ggpushxto0 ( H , 1 ) ;
2015-08-08 13:57:52 +00:00
}
2020-07-27 16:49:04 +00:00
EX shiftmatrix rgpushxto0 ( const shiftpoint & H ) {
return shiftless ( rgpushxto0 ( H . h ) , H . shift ) ;
}
2019-08-22 11:45:51 +00:00
/** \brief Fix the numerical inaccuracies in the isometry T
2020-03-27 20:47:09 +00:00
*
2019-08-22 11:45:51 +00:00
* The nature of hyperbolic geometry makes the computations numerically unstable .
* The numerical errors tend to accumulate , eventually destroying the projection .
* This function fixes this problem by replacing T with a ' correct ' isometry .
*/
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX void fixmatrix ( transmatrix & T ) {
2019-08-06 10:00:46 +00:00
if ( nonisotropic ) ; // T may be inverse... do not do that
2020-05-15 09:38:20 +00:00
else if ( cgflags & qAFFINE ) ; // affine
2022-12-08 18:38:06 +00:00
else if ( gproduct ) {
2019-08-18 20:28:10 +00:00
auto z = zlevel ( tC0 ( T ) ) ;
2022-12-06 00:04:26 +00:00
T = scale_matrix ( T , exp ( - z ) ) ;
2019-08-18 20:28:10 +00:00
PIU ( fixmatrix ( T ) ) ;
2022-12-06 00:04:26 +00:00
T = scale_matrix ( T , exp ( + z ) ) ;
2019-08-18 20:28:10 +00:00
}
2020-05-15 20:53:24 +00:00
else if ( euclid )
fixmatrix_euclid ( T ) ;
else
orthonormalize ( T ) ;
}
EX void fixmatrix_euclid ( transmatrix & T ) {
for ( int x = 0 ; x < GDIM ; x + + ) for ( int y = 0 ; y < = x ; y + + ) {
ld dp = 0 ;
for ( int z = 0 ; z < GDIM ; z + + ) dp + = T [ z ] [ x ] * T [ z ] [ y ] ;
if ( y = = x ) dp = 1 - sqrt ( 1 / dp ) ;
for ( int z = 0 ; z < GDIM ; z + + ) T [ z ] [ x ] - = dp * T [ z ] [ y ] ;
2016-01-02 10:09:13 +00:00
}
2020-05-15 20:53:24 +00:00
for ( int x = 0 ; x < GDIM ; x + + ) T [ LDIM ] [ x ] = 0 ;
T [ LDIM ] [ LDIM ] = 1 ;
}
EX void orthonormalize ( transmatrix & T ) {
for ( int x = 0 ; x < MDIM ; x + + ) for ( int y = 0 ; y < = x ; y + + ) {
2015-08-08 13:57:52 +00:00
ld dp = 0 ;
2020-11-01 16:37:51 +00:00
for ( int z = 0 ; z < MXDIM ; z + + ) dp + = T [ z ] [ x ] * T [ z ] [ y ] * sig ( z ) ;
2015-08-08 13:57:52 +00:00
if ( y = = x ) dp = 1 - sqrt ( sig ( x ) / dp ) ;
2020-11-01 16:37:51 +00:00
for ( int z = 0 ; z < MXDIM ; z + + ) T [ z ] [ x ] - = dp * T [ z ] [ y ] ;
2015-08-08 13:57:52 +00:00
}
}
2020-11-14 14:04:52 +00:00
/** fix a 3D rotation matrix */
EX void fix_rotation ( transmatrix & rot ) {
dynamicval < eGeometry > g ( geometry , gSphere ) ;
fixmatrix ( rot ) ;
for ( int i = 0 ; i < 3 ; i + + ) rot [ i ] [ 3 ] = rot [ 3 ] [ i ] = 0 ;
rot [ 3 ] [ 3 ] = 1 ;
}
2020-08-20 14:11:35 +00:00
/** determinant 2x2 */
EX ld det2 ( const transmatrix & T ) {
return T [ 0 ] [ 0 ] * T [ 1 ] [ 1 ] - T [ 0 ] [ 1 ] * T [ 1 ] [ 0 ] ;
}
/** determinant 3x3 */
EX ld det3 ( const transmatrix & T ) {
ld det = 0 ;
for ( int i = 0 ; i < 3 ; i + + )
det + = T [ 0 ] [ i ] * T [ 1 ] [ ( i + 1 ) % 3 ] * T [ 2 ] [ ( i + 2 ) % 3 ] ;
for ( int i = 0 ; i < 3 ; i + + )
det - = T [ 0 ] [ i ] * T [ 1 ] [ ( i + 2 ) % 3 ] * T [ 2 ] [ ( i + 1 ) % 3 ] ;
return det ;
}
2019-08-22 11:45:51 +00:00
/** determinant */
2019-08-09 19:00:52 +00:00
EX ld det ( const transmatrix & T ) {
2020-10-06 19:09:03 +00:00
if ( MDIM = = 3 )
2020-08-20 14:12:04 +00:00
return det3 ( T ) ;
2019-02-24 18:40:01 +00:00
else {
ld det = 1 ;
transmatrix M = T ;
for ( int a = 0 ; a < MDIM ; a + + ) {
2021-04-07 12:46:46 +00:00
int max_at = a ;
2020-10-06 19:09:03 +00:00
for ( int b = a ; b < MDIM ; b + + )
2021-04-07 12:46:46 +00:00
if ( abs ( M [ b ] [ a ] ) > abs ( M [ max_at ] [ a ] ) )
max_at = b ;
if ( max_at ! = a )
for ( int c = a ; c < MDIM ; c + + ) tie ( M [ max_at ] [ c ] , M [ a ] [ c ] ) = make_pair ( - M [ a ] [ c ] , M [ max_at ] [ c ] ) ;
2019-02-24 18:40:01 +00:00
if ( ! M [ a ] [ a ] ) return 0 ;
2020-10-06 19:09:03 +00:00
for ( int b = a + 1 ; b < MDIM ; b + + ) {
2019-02-24 18:40:01 +00:00
ld co = - M [ b ] [ a ] / M [ a ] [ a ] ;
for ( int c = a ; c < MDIM ; c + + ) M [ b ] [ c ] + = M [ a ] [ c ] * co ;
2019-02-17 17:47:19 +00:00
}
2019-02-24 18:40:01 +00:00
det * = M [ a ] [ a ] ;
2019-02-17 17:47:19 +00:00
}
2019-02-24 18:40:01 +00:00
return det ;
2019-02-17 17:47:19 +00:00
}
2017-07-22 23:33:27 +00:00
}
2018-08-19 20:54:11 +00:00
2019-08-22 11:45:51 +00:00
/** warning about incorrect inverse */
2018-08-19 20:54:11 +00:00
void inverse_error ( const transmatrix & T ) {
2018-11-24 16:01:49 +00:00
println ( hlog , " Warning: inverting a singular matrix: " , T ) ;
2018-08-19 20:54:11 +00:00
}
2020-09-16 03:57:05 +00:00
/** inverse of a 3x3 matrix */
2020-08-08 14:05:49 +00:00
EX transmatrix inverse3 ( const transmatrix & T ) {
ld d = det ( T ) ;
transmatrix T2 ;
if ( d = = 0 ) {
inverse_error ( T ) ;
return Id ;
2018-01-13 18:21:08 +00:00
}
2020-08-08 14:05:49 +00:00
for ( int i = 0 ; i < 3 ; i + + )
for ( int j = 0 ; j < 3 ; j + + )
T2 [ j ] [ i ] = ( T [ ( i + 1 ) % 3 ] [ ( j + 1 ) % 3 ] * T [ ( i + 2 ) % 3 ] [ ( j + 2 ) % 3 ] - T [ ( i + 1 ) % 3 ] [ ( j + 2 ) % 3 ] * T [ ( i + 2 ) % 3 ] [ ( j + 1 ) % 3 ] ) / d ;
return T2 ;
}
2020-09-16 03:57:05 +00:00
/** \brief inverse of a general matrix */
2020-08-08 14:05:49 +00:00
EX transmatrix inverse ( const transmatrix & T ) {
if ( MDIM = = 3 )
return inverse3 ( T ) ;
2019-02-24 18:40:01 +00:00
else {
transmatrix T1 = T ;
transmatrix T2 = Id ;
2015-08-08 13:57:52 +00:00
2019-02-24 18:40:01 +00:00
for ( int a = 0 ; a < MDIM ; a + + ) {
2019-02-25 03:05:56 +00:00
int best = a ;
for ( int b = a + 1 ; b < MDIM ; b + + )
if ( abs ( T1 [ b ] [ a ] ) > abs ( T1 [ best ] [ a ] ) )
best = b ;
int b = best ;
if ( b ! = a )
for ( int c = 0 ; c < MDIM ; c + + )
swap ( T1 [ b ] [ c ] , T1 [ a ] [ c ] ) , swap ( T2 [ b ] [ c ] , T2 [ a ] [ c ] ) ;
2019-02-24 18:40:01 +00:00
if ( ! T1 [ a ] [ a ] ) { inverse_error ( T ) ; return Id ; }
2019-05-08 16:33:08 +00:00
for ( int b = a + 1 ; b < = GDIM ; b + + ) {
2019-02-24 18:40:01 +00:00
ld co = - T1 [ b ] [ a ] / T1 [ a ] [ a ] ;
for ( int c = 0 ; c < MDIM ; c + + ) T1 [ b ] [ c ] + = T1 [ a ] [ c ] * co , T2 [ b ] [ c ] + = T2 [ a ] [ c ] * co ;
2019-02-17 17:47:19 +00:00
}
}
2019-02-24 18:40:01 +00:00
for ( int a = MDIM - 1 ; a > = 0 ; a - - ) {
for ( int b = 0 ; b < a ; b + + ) {
ld co = - T1 [ b ] [ a ] / T1 [ a ] [ a ] ;
for ( int c = 0 ; c < MDIM ; c + + ) T1 [ b ] [ c ] + = T1 [ a ] [ c ] * co , T2 [ b ] [ c ] + = T2 [ a ] [ c ] * co ;
}
ld co = 1 / T1 [ a ] [ a ] ;
for ( int c = 0 ; c < MDIM ; c + + ) T1 [ a ] [ c ] * = co , T2 [ a ] [ c ] * = co ;
2019-02-17 17:47:19 +00:00
}
2019-02-24 18:40:01 +00:00
return T2 ;
2019-02-17 17:47:19 +00:00
}
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
2020-09-16 03:57:05 +00:00
/** \brief inverse of an orthogonal matrix, i.e., transposition */
EX transmatrix ortho_inverse ( transmatrix T ) {
for ( int i = 1 ; i < MDIM ; i + + )
for ( int j = 0 ; j < i ; j + + )
swap ( T [ i ] [ j ] , T [ j ] [ i ] ) ;
return T ;
}
/** \brief inverse of an orthogonal matrix in Minkowski space */
EX transmatrix pseudo_ortho_inverse ( transmatrix T ) {
2020-11-01 16:37:51 +00:00
for ( int i = 1 ; i < MXDIM ; i + + )
2020-09-16 03:57:05 +00:00
for ( int j = 0 ; j < i ; j + + )
swap ( T [ i ] [ j ] , T [ j ] [ i ] ) ;
for ( int i = 0 ; i < MDIM - 1 ; i + + )
T [ i ] [ MDIM - 1 ] = - T [ i ] [ MDIM - 1 ] ,
T [ MDIM - 1 ] [ i ] = - T [ MDIM - 1 ] [ i ] ;
return T ;
}
/** \brief inverse of an isometry -- in most geometries this can be done more efficiently than using inverse */
EX transmatrix iso_inverse ( const transmatrix & T ) {
if ( hyperbolic )
return pseudo_ortho_inverse ( T ) ;
if ( sphere )
return ortho_inverse ( T ) ;
2020-09-16 15:35:16 +00:00
if ( nil ) {
transmatrix U = Id ;
U [ 2 ] [ LDIM ] = T [ 0 ] [ LDIM ] * T [ 1 ] [ LDIM ] - T [ 2 ] [ LDIM ] ;
U [ 1 ] [ LDIM ] = - T [ 1 ] [ LDIM ] ;
U [ 2 ] [ 1 ] = U [ 0 ] [ LDIM ] = - T [ 0 ] [ LDIM ] ;
return U ;
}
2020-09-16 03:57:05 +00:00
if ( euclid & & ! ( cgflags & qAFFINE ) ) {
transmatrix U = Id ;
for ( int i = 0 ; i < MDIM - 1 ; i + + )
for ( int j = 0 ; j < MDIM - 1 ; j + + )
U [ i ] [ j ] = T [ j ] [ i ] ;
hyperpoint h = U * tC0 ( T ) ;
for ( int i = 0 ; i < MDIM - 1 ; i + + )
U [ i ] [ MDIM - 1 ] = - h [ i ] ;
return U ;
}
return inverse ( T ) ;
}
/** \brief T inverse a matrix T = O*S, where O is isometry and S is a scaling matrix (todo optimize) */
EX transmatrix z_inverse ( const transmatrix & T ) {
return inverse ( T ) ;
}
/** \brief T inverse a matrix T = O*P, where O is orthogonal and P is an isometry (todo optimize) */
EX transmatrix view_inverse ( transmatrix T ) {
2020-09-16 15:35:51 +00:00
if ( nonisotropic ) return inverse ( T ) ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) return z_inverse ( T ) ;
2020-09-16 15:35:51 +00:00
return iso_inverse ( T ) ;
2020-09-16 03:57:05 +00:00
}
/** \brief T inverse a matrix T = P*O, where O is orthogonal and P is an isometry (todo optimize) */
EX transmatrix iview_inverse ( transmatrix T ) {
2020-09-16 15:35:51 +00:00
if ( nonisotropic ) return inverse ( T ) ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) return z_inverse ( T ) ;
2020-09-16 15:35:51 +00:00
return iso_inverse ( T ) ;
2020-09-16 03:57:05 +00:00
}
2019-08-17 21:28:41 +00:00
EX pair < ld , hyperpoint > product_decompose ( hyperpoint h ) {
ld z = zlevel ( h ) ;
2022-12-06 00:04:26 +00:00
return make_pair ( z , scale_point ( h , exp ( - z ) ) ) ;
2019-08-17 21:28:41 +00:00
}
2019-08-22 11:45:51 +00:00
/** distance from mh and 0 */
2019-08-09 19:00:52 +00:00
EX ld hdist0 ( const hyperpoint & mh ) {
2018-02-27 18:21:43 +00:00
switch ( cgclass ) {
case gcHyperbolic :
2019-08-17 21:28:41 +00:00
if ( mh [ LDIM ] < 1 ) return 0 ;
return acosh ( mh [ LDIM ] ) ;
2018-02-27 18:21:43 +00:00
case gcEuclid : {
2019-05-08 16:33:08 +00:00
return hypot_d ( GDIM , mh ) ;
2018-02-27 18:21:43 +00:00
}
case gcSphere : {
2019-08-17 21:28:41 +00:00
ld res = mh [ LDIM ] > = 1 ? 0 : mh [ LDIM ] < = - 1 ? M_PI : acos ( mh [ LDIM ] ) ;
2018-02-27 18:21:43 +00:00
return res ;
}
2019-08-17 21:28:41 +00:00
case gcProduct : {
auto d1 = product_decompose ( mh ) ;
return hypot ( PIU ( hdist0 ( d1 . second ) ) , d1 . first ) ;
}
2021-03-30 09:27:48 +00:00
# if MAXMDIM >= 4
2019-08-24 09:55:45 +00:00
case gcSL2 : {
auto cosh_r = hypot ( mh [ 2 ] , mh [ 3 ] ) ;
auto phi = atan2 ( mh [ 2 ] , mh [ 3 ] ) ;
return hypot ( cosh_r < 1 ? 0 : acosh ( cosh_r ) , phi ) ;
}
2019-10-06 09:51:10 +00:00
case gcNil : {
ld bz = mh [ 0 ] * mh [ 1 ] / 2 ;
return hypot ( mh [ 0 ] , mh [ 1 ] ) + abs ( mh [ 2 ] - bz ) ;
}
2021-03-30 09:27:48 +00:00
# endif
2018-02-27 18:21:43 +00:00
default :
2019-07-23 13:08:07 +00:00
return hypot_d ( GDIM , mh ) ;
2017-05-31 16:33:50 +00:00
}
2017-03-23 10:53:57 +00:00
}
2020-07-27 16:49:04 +00:00
EX ld hdist0 ( const shiftpoint & mh ) {
return hdist0 ( unshift ( mh ) ) ;
}
2019-08-22 11:45:51 +00:00
/** length of a circle of radius r */
2019-08-09 19:00:52 +00:00
EX ld circlelength ( ld r ) {
2018-02-27 18:21:43 +00:00
switch ( cgclass ) {
case gcEuclid :
2022-11-12 21:38:45 +00:00
return TAU * r ;
2018-02-27 18:21:43 +00:00
case gcHyperbolic :
2022-11-12 21:38:45 +00:00
return TAU * sinh ( r ) ;
2018-02-27 18:21:43 +00:00
case gcSphere :
2022-11-12 21:38:45 +00:00
return TAU * sin ( r ) ;
2018-02-27 18:21:43 +00:00
default :
2022-11-12 21:38:45 +00:00
return TAU * r ;
2018-02-27 18:21:43 +00:00
}
2018-01-04 11:25:02 +00:00
}
2019-08-22 11:45:51 +00:00
/* distance between h1 and h2 */
2019-08-09 19:00:52 +00:00
EX ld hdist ( const hyperpoint & h1 , const hyperpoint & h2 ) {
2018-02-27 18:21:43 +00:00
ld iv = intval ( h1 , h2 ) ;
switch ( cgclass ) {
case gcEuclid :
2019-03-02 23:45:56 +00:00
if ( iv < 0 ) return 0 ;
2018-02-27 18:21:43 +00:00
return sqrt ( iv ) ;
case gcHyperbolic :
2019-03-02 23:45:56 +00:00
if ( iv < 0 ) return 0 ;
2018-02-27 18:21:43 +00:00
return 2 * asinh ( sqrt ( iv ) / 2 ) ;
case gcSphere :
2018-05-01 17:35:09 +00:00
return 2 * asin_auto_clamp ( sqrt ( iv ) / 2 ) ;
2019-08-17 21:28:41 +00:00
case gcProduct : {
auto d1 = product_decompose ( h1 ) ;
auto d2 = product_decompose ( h2 ) ;
return hypot ( PIU ( hdist ( d1 . second , d2 . second ) ) , d1 . first - d2 . first ) ;
}
2019-08-24 18:29:10 +00:00
case gcSL2 :
2020-09-16 03:57:05 +00:00
return hdist0 ( stretch : : itranslate ( h1 ) * h2 ) ;
2018-02-27 18:21:43 +00:00
default :
2019-07-23 13:08:07 +00:00
if ( iv < 0 ) return 0 ;
return sqrt ( iv ) ;
2018-02-27 18:21:43 +00:00
}
2016-08-26 09:58:03 +00:00
}
2020-07-27 16:49:04 +00:00
EX ld hdist ( const shiftpoint & h1 , const shiftpoint & h2 ) {
return hdist ( h1 . h , unshift ( h2 , h1 . shift ) ) ;
}
2022-12-06 00:04:26 +00:00
/** like orthogonal_move but fol may be factor (in 2D graphics) or level (elsewhere) */
EX hyperpoint orthogonal_move_fol ( const hyperpoint & h , double fol ) {
if ( GDIM = = 2 ) return scale_point ( h , fol ) ;
else return orthogonal_move ( h , fol ) ;
2017-03-23 10:53:57 +00:00
}
2022-12-06 00:04:26 +00:00
/** like orthogonal_move but fol may be factor (in 2D graphics) or level (elsewhere) */
EX transmatrix orthogonal_move_fol ( const transmatrix & T , double fol ) {
if ( GDIM = = 2 ) return scale_matrix ( T , fol ) ;
else return orthogonal_move ( T , fol ) ;
2020-07-27 16:49:04 +00:00
}
2022-12-06 00:04:26 +00:00
/** like orthogonal_move but fol may be factor (in 2D graphics) or level (elsewhere) */
EX shiftmatrix orthogonal_move_fol ( const shiftmatrix & T , double fol ) {
if ( GDIM = = 2 ) return scale_matrix ( T , fol ) ;
else return orthogonal_move ( T , fol ) ;
}
/** the scaling matrix (Euclidean homogeneous scaling; also shift by log(scale) in product space */
EX transmatrix scale_matrix ( const transmatrix & t , ld scale_factor ) {
2017-03-23 10:53:57 +00:00
transmatrix res ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) {
for ( int j = 0 ; j < MDIM ; j + + )
2022-12-06 00:04:26 +00:00
res [ i ] [ j ] = t [ i ] [ j ] * scale_factor ;
2020-11-01 16:37:51 +00:00
for ( int j = MDIM ; j < MXDIM ; j + + )
res [ i ] [ j ] = t [ i ] [ j ] ;
}
2017-03-23 10:53:57 +00:00
return res ;
}
2022-12-06 00:04:26 +00:00
/** the scaling matrix (Euclidean homogeneous scaling; also shift by log(scale) in product space */
EX shiftmatrix scale_matrix ( const shiftmatrix & t , ld scale_factor ) {
return shiftless ( scale_matrix ( t . T , scale_factor ) , t . shift ) ;
}
/** the scaling matrix (Euclidean homogeneous scaling; also shift by log(scale) in product space */
EX hyperpoint scale_point ( const hyperpoint & h , ld scale_factor ) {
hyperpoint res ;
for ( int j = 0 ; j < MDIM ; j + + )
res [ j ] = h [ j ] * scale_factor ;
for ( int j = MDIM ; j < MXDIM ; j + + )
res [ j ] = h [ j ] ;
return res ;
}
2022-12-09 01:25:02 +00:00
EX bool moved_center ( ) {
if ( geom3 : : sph_in_euc ( ) ) return true ;
if ( geom3 : : sph_in_hyp ( ) ) return true ;
return false ;
}
2022-12-08 18:38:06 +00:00
/** Returns the intended center of the tile, relative to its local matrix. Usually C0 but may be different, e.g. when embedding a sphere in E3 or H3. */
EX hyperpoint tile_center ( ) {
if ( geom3 : : sph_in_euc ( ) ) return C02 + C03 ;
if ( geom3 : : sph_in_hyp ( ) ) return zpush0 ( 1 ) ;
return C0 ;
}
2022-12-06 00:04:26 +00:00
EX transmatrix orthogonal_move ( const transmatrix & t , double level ) {
2022-12-08 18:38:06 +00:00
if ( gproduct ) return scale_matrix ( t , exp ( level ) ) ;
if ( GDIM = = 3 ) return t * lzpush ( level ) ;
2022-12-06 00:04:26 +00:00
return scale_matrix ( t , geom3 : : lev_to_factor ( level ) ) ;
}
EX shiftmatrix orthogonal_move ( const shiftmatrix & t , double level ) {
return shiftless ( orthogonal_move ( t . T , level ) , t . shift ) ;
2020-07-27 16:49:04 +00:00
}
2019-08-09 19:00:52 +00:00
EX transmatrix xyscale ( const transmatrix & t , double fac ) {
2017-03-23 10:53:57 +00:00
transmatrix res ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) {
for ( int j = 0 ; j < GDIM ; j + + )
res [ i ] [ j ] = t [ i ] [ j ] * fac ;
for ( int j = GDIM ; j < MXDIM ; j + + )
res [ i ] [ j ] = t [ i ] [ j ] ;
}
2017-03-23 10:53:57 +00:00
return res ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix xyzscale ( const transmatrix & t , double fac , double facz ) {
2017-03-23 10:53:57 +00:00
transmatrix res ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) {
for ( int j = 0 ; j < GDIM ; j + + )
res [ i ] [ j ] = t [ i ] [ j ] * fac ;
res [ i ] [ LDIM ] =
t [ i ] [ LDIM ] * facz ;
for ( int j = LDIM + 1 ; j < MXDIM ; j + + )
res [ i ] [ j ] = t [ i ] [ j ] ;
}
2017-03-23 10:53:57 +00:00
return res ;
}
2020-07-27 16:49:04 +00:00
EX shiftmatrix xyzscale ( const shiftmatrix & t , double fac , double facz ) {
return shiftless ( xyzscale ( t . T , fac , facz ) , t . shift ) ;
}
2019-08-09 19:00:52 +00:00
EX transmatrix mzscale ( const transmatrix & t , double fac ) {
2019-05-08 16:33:08 +00:00
if ( GDIM = = 3 ) return t * cpush ( 2 , fac ) ;
2017-03-23 10:53:57 +00:00
// take only the spin
transmatrix tcentered = gpushxto0 ( tC0 ( t ) ) * t ;
// tcentered = tcentered * spin(downspin_zivory);
fac - = 1 ;
transmatrix res = t * inverse ( tcentered ) * ypush ( - fac ) * tcentered ;
fac * = .2 ;
fac + = 1 ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) for ( int j = 0 ; j < MXDIM ; j + + )
2017-03-23 10:53:57 +00:00
res [ i ] [ j ] = res [ i ] [ j ] * fac ;
return res ;
}
2020-07-27 16:49:04 +00:00
EX shiftmatrix mzscale ( const shiftmatrix & t , double fac ) {
return shiftless ( mzscale ( t . T , fac ) , t . shift ) ;
}
2019-08-09 19:00:52 +00:00
EX hyperpoint mid3 ( hyperpoint h1 , hyperpoint h2 , hyperpoint h3 ) {
2018-08-04 20:35:15 +00:00
return mid ( h1 + h2 + h3 , h1 + h2 + h3 ) ;
}
2019-08-09 19:00:52 +00:00
EX hyperpoint mid_at ( hyperpoint h1 , hyperpoint h2 , ld v ) {
2018-08-04 20:35:15 +00:00
hyperpoint h = h1 * ( 1 - v ) + h2 * v ;
return mid ( h , h ) ;
}
2019-08-09 19:00:52 +00:00
EX hyperpoint mid_at_actual ( hyperpoint h , ld v ) {
2018-08-19 14:28:36 +00:00
return rspintox ( h ) * xpush0 ( hdist0 ( h ) * v ) ;
2018-08-05 03:07:34 +00:00
}
2019-08-22 11:45:51 +00:00
/** in 3D, an orthogonal projection of C0 on the given triangle */
2019-08-09 19:00:52 +00:00
EX hyperpoint orthogonal_of_C0 ( hyperpoint h0 , hyperpoint h1 , hyperpoint h2 ) {
2019-04-07 01:08:43 +00:00
h0 / = h0 [ 3 ] ;
h1 / = h1 [ 3 ] ;
h2 / = h2 [ 3 ] ;
hyperpoint w = h0 ;
hyperpoint d1 = h1 - h0 ;
hyperpoint d2 = h2 - h0 ;
ld denom = ( d1 | d1 ) * ( d2 | d2 ) - ( d1 | d2 ) * ( d1 | d2 ) ;
ld a1 = ( d2 | w ) * ( d1 | d2 ) - ( d1 | w ) * ( d2 | d2 ) ;
ld a2 = ( d1 | w ) * ( d1 | d2 ) - ( d2 | w ) * ( d1 | d1 ) ;
hyperpoint h = w * denom + d1 * a1 + d2 * a2 ;
return normalize ( h ) ;
}
2019-08-09 19:00:52 +00:00
EX hyperpoint hpxd ( ld d , ld x , ld y , ld z ) {
2019-05-26 16:04:02 +00:00
hyperpoint H = hpxyz ( d * x , d * y , z ) ;
H = mid ( H , H ) ;
return H ;
}
2019-08-09 19:00:52 +00:00
EX ld signum ( ld x ) { return x < 0 ? - 1 : x > 0 ? 1 : 0 ; }
2019-05-26 16:04:02 +00:00
2019-09-05 10:00:55 +00:00
EX bool asign ( ld y1 , ld y2 ) { return signum ( y1 ) ! = signum ( y2 ) ; }
2019-05-26 16:04:02 +00:00
2019-09-05 10:00:55 +00:00
EX ld xcross ( ld x1 , ld y1 , ld x2 , ld y2 ) { return x1 + ( x2 - x1 ) * y1 / ( y1 - y2 ) ; }
2019-05-26 16:04:02 +00:00
2022-12-17 10:16:47 +00:00
# if HDR
enum eShiftMethod { smProduct , smIsometric , smEmbedded , smLie , smGeodesic } ;
enum eEmbeddedShiftMethodChoice { smcNone , smcBoth , smcAuto } ;
2022-12-17 10:47:10 +00:00
enum eShiftMethodApplication { smaManualCamera , smaAutocenter , smaObject , smaWallRadar } ;
2022-12-17 10:16:47 +00:00
# endif
EX eEmbeddedShiftMethodChoice embedded_shift_method_choice = smcBoth ;
2022-12-17 10:47:10 +00:00
EX bool use_embedded_shift ( eShiftMethodApplication sma ) {
switch ( sma ) {
case smaAutocenter : return embedded_shift_method_choice ! = smcNone ;
case smaManualCamera : return embedded_shift_method_choice = = smcBoth ;
case smaObject : return true ;
case smaWallRadar : return among ( pmodel , mdLiePerspective , mdLieOrthogonal ) ;
default : throw hr_exception ( " unknown sma " ) ;
}
2022-12-17 10:16:47 +00:00
}
2022-12-17 10:47:10 +00:00
EX eShiftMethod shift_method ( eShiftMethodApplication sma ) {
2022-12-17 10:16:47 +00:00
if ( gproduct ) return smProduct ;
2022-12-17 10:47:10 +00:00
if ( embedded_plane & & use_embedded_shift ( sma ) ) return nonisotropic ? smLie : smEmbedded ;
2022-12-17 10:16:47 +00:00
if ( ! nonisotropic & & ! stretch : : in ( ) ) return smIsometric ;
if ( ! nisot : : geodesic_movement & & ! embedded_plane ) return smLie ;
return smGeodesic ;
}
2022-12-17 10:47:10 +00:00
EX transmatrix shift_object ( const transmatrix Position , const transmatrix & ori , const hyperpoint direction , eShiftMethod sm IS ( shift_method ( smaObject ) ) ) {
2022-12-17 10:35:28 +00:00
switch ( sm ) {
case smGeodesic :
return nisot : : parallel_transport ( Position , direction ) ;
case smLie :
return nisot : : lie_transport ( Position , direction ) ;
case smProduct : {
hyperpoint h = product : : direct_exp ( ori * direction ) ;
return Position * rgpushxto0 ( h ) ;
}
case smIsometric : {
return Position * rgpushxto0 ( direct_exp ( direction ) ) ;
}
case smEmbedded : {
throw hr_exception ( " not implemented " ) ;
}
2019-08-26 07:06:39 +00:00
}
}
2022-12-17 10:47:10 +00:00
EX void apply_shift_object ( transmatrix & Position , const transmatrix orientation , const hyperpoint direction , eShiftMethod sm IS ( shift_method ( smaObject ) ) ) {
2022-12-17 10:35:28 +00:00
Position = shift_object ( Position , orientation , direction , sm ) ;
2019-08-20 16:02:03 +00:00
}
2019-08-26 07:06:39 +00:00
EX void rotate_object ( transmatrix & Position , transmatrix & orientation , transmatrix R ) {
2022-12-08 18:38:06 +00:00
if ( gproduct ) orientation = orientation * R ;
2019-08-26 07:06:39 +00:00
else Position = Position * R ;
2019-07-31 13:19:40 +00:00
}
2019-08-26 07:06:39 +00:00
EX transmatrix spin_towards ( const transmatrix Position , transmatrix & ori , const hyperpoint goal , int dir , int back ) {
transmatrix T ;
ld alpha = 0 ;
2019-08-27 20:02:16 +00:00
if ( nonisotropic & & nisot : : geodesic_movement )
2019-08-26 07:06:39 +00:00
T = nisot : : spin_towards ( Position , goal ) ;
else {
hyperpoint U = inverse ( Position ) * goal ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) {
2019-08-26 07:06:39 +00:00
hyperpoint h = product : : inverse_exp ( U ) ;
alpha = asin_clamp ( h [ 2 ] / hypot_d ( 3 , h ) ) ;
U = product_decompose ( U ) . second ;
}
T = rspintox ( U ) ;
}
2022-11-12 21:38:45 +00:00
if ( back < 0 ) T = T * spin180 ( ) , alpha = - alpha ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) {
2019-08-26 07:06:39 +00:00
if ( dir = = 0 ) ori = cspin ( 2 , 0 , alpha ) ;
2022-11-12 21:38:45 +00:00
if ( dir = = 2 ) ori = cspin ( 2 , 0 , alpha - 90. _deg ) , dir = 0 ;
2019-08-26 07:06:39 +00:00
}
2022-11-12 21:38:45 +00:00
if ( dir ) T = T * cspin ( dir , 0 , - 90. _deg ) ;
2019-07-31 15:05:12 +00:00
T = Position * T ;
return T ;
2019-07-31 13:19:40 +00:00
}
2020-07-27 16:49:04 +00:00
EX shiftmatrix spin_towards ( const shiftmatrix Position , transmatrix & ori , const shiftpoint goal , int dir , int back ) {
return shiftless ( spin_towards ( Position . T , ori , unshift ( goal , Position . shift ) , dir , back ) , Position . shift ) ;
}
2019-08-09 19:00:52 +00:00
EX ld ortho_error ( transmatrix T ) {
2019-08-03 10:59:08 +00:00
ld err = 0 ;
for ( int x = 0 ; x < 3 ; x + + ) for ( int y = 0 ; y < 3 ; y + + ) {
ld s = 0 ;
for ( int z = 0 ; z < 3 ; z + + ) s + = T [ z ] [ x ] * T [ z ] [ y ] ;
s - = ( x = = y ) ;
err + = s * s ;
}
return err ;
}
2019-08-09 22:29:03 +00:00
EX transmatrix transpose ( transmatrix T ) {
transmatrix result ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + )
for ( int j = 0 ; j < MXDIM ; j + + )
2019-08-09 22:29:03 +00:00
result [ j ] [ i ] = T [ i ] [ j ] ;
return result ;
}
2022-12-08 18:38:06 +00:00
EX hyperpoint lspinpush0 ( ld alpha , ld x ) {
2022-12-08 21:06:44 +00:00
bool f = embedded_plane ;
if ( f ) geom3 : : light_flip ( true ) ;
if ( embedded_plane ) throw hr_exception ( " still embedded plane " ) ;
2022-12-08 18:38:06 +00:00
hyperpoint h = xspinpush0 ( alpha , x ) ;
2022-12-08 21:06:44 +00:00
if ( f ) geom3 : : light_flip ( false ) ;
if ( f ) swapmatrix ( h ) ;
2022-12-08 18:38:06 +00:00
return h ;
}
2019-08-09 22:28:28 +00:00
# if HDR
2019-08-24 09:55:45 +00:00
namespace slr {
hyperpoint xyz_point ( ld x , ld y , ld z ) ;
hyperpoint polar ( ld r , ld theta , ld phi ) ;
}
2019-08-09 22:28:28 +00:00
inline hyperpoint cpush0 ( int c , ld x ) {
hyperpoint h = Hypc ;
2019-08-24 09:55:45 +00:00
if ( sl2 ) return slr : : xyz_point ( c = = 0 ? x : 0 , c = = 1 ? x : 0 , c = = 2 ? x : 0 ) ;
2022-12-08 18:38:06 +00:00
if ( c = = 2 & & gproduct ) {
2019-08-19 08:32:23 +00:00
h [ 2 ] = exp ( x ) ;
return h ;
}
2019-08-17 21:28:41 +00:00
h [ LDIM ] = cos_auto ( x ) ;
2019-08-09 22:28:28 +00:00
h [ c ] = sin_auto ( x ) ;
return h ;
}
inline hyperpoint xpush0 ( ld x ) { return cpush0 ( 0 , x ) ; }
2022-12-08 18:38:06 +00:00
inline hyperpoint lxpush0 ( ld x ) { return lxpush ( x ) * tile_center ( ) ; }
2019-08-09 22:28:28 +00:00
inline hyperpoint ypush0 ( ld x ) { return cpush0 ( 1 , x ) ; }
2019-08-20 12:56:00 +00:00
inline hyperpoint zpush0 ( ld x ) { return cpush0 ( 2 , x ) ; }
2019-08-09 22:28:28 +00:00
2019-08-22 11:45:51 +00:00
/** T * C0, optimized */
2019-08-09 22:28:28 +00:00
inline hyperpoint tC0 ( const transmatrix & T ) {
hyperpoint z ;
2020-11-01 16:37:51 +00:00
for ( int i = 0 ; i < MXDIM ; i + + ) z [ i ] = T [ i ] [ LDIM ] ;
2019-08-09 22:28:28 +00:00
return z ;
}
2020-07-27 16:49:04 +00:00
inline hyperpoint tC0_t ( const transmatrix & T ) { return tC0 ( T ) ; }
inline shiftpoint tC0 ( const shiftmatrix & T ) {
return shiftpoint { tC0 ( T . T ) , T . shift } ;
}
2019-08-09 22:28:28 +00:00
# endif
2022-12-08 21:06:44 +00:00
EX hyperpoint xspinpush0 ( ld alpha , ld x ) {
if ( sl2 ) return slr : : polar ( x , - alpha , 0 ) ;
if ( embedded_plane ) return lspinpush0 ( alpha , x ) ;
hyperpoint h = Hypc ;
h [ LDIM ] = cos_auto ( x ) ;
h [ 0 ] = sin_auto ( x ) * cos ( alpha ) ;
h [ 1 ] = sin_auto ( x ) * - sin ( alpha ) ;
return h ;
}
2019-08-24 16:14:38 +00:00
/** tangent vector in the given direction */
EX hyperpoint ctangent ( int c , ld x ) { return point3 ( c = = 0 ? x : 0 , c = = 1 ? x : 0 , c = = 2 ? x : 0 ) ; }
/** tangent vector in direction X */
EX hyperpoint xtangent ( ld x ) { return ctangent ( 0 , x ) ; }
2022-12-08 18:38:06 +00:00
/** tangent vector in direction Z */
2019-08-24 16:14:38 +00:00
EX hyperpoint ztangent ( ld z ) { return ctangent ( 2 , z ) ; }
2022-12-08 18:38:06 +00:00
/** tangent vector in logical direction Z */
2022-12-15 17:15:26 +00:00
EX hyperpoint lztangent ( ld z ) {
if ( geom3 : : hyp_in_solnih ( ) ) return ctangent ( 0 , z ) ;
if ( geom3 : : euc_in_nil ( ) ) return ctangent ( 1 , z ) ;
return ctangent ( 2 , z ) ;
}
2022-12-08 18:38:06 +00:00
2019-08-24 16:14:38 +00:00
/** change the length of the targent vector */
EX hyperpoint tangent_length ( hyperpoint dir , ld length ) {
ld r = hypot_d ( GDIM , dir ) ;
if ( ! r ) return dir ;
return dir * ( length / r ) ;
}
/** exponential function: follow the geodesic given by v */
2020-04-11 13:08:24 +00:00
EX hyperpoint direct_exp ( hyperpoint v ) {
2021-03-30 09:27:48 +00:00
# if CAP_SOLV
2020-04-11 13:08:24 +00:00
if ( sn : : in ( ) ) return nisot : : numerical_exp ( v ) ;
2021-03-30 09:27:48 +00:00
# endif
# if MAXMDIM >= 4
2019-08-24 16:14:38 +00:00
if ( nil ) return nilv : : formula_exp ( v ) ;
2020-08-08 14:08:51 +00:00
if ( sl2 | | stretch : : in ( ) ) return stretch : : mstretch ? nisot : : numerical_exp ( v ) : rots : : formula_exp ( v ) ;
2021-03-30 09:27:48 +00:00
# endif
2022-12-08 18:38:06 +00:00
if ( gproduct ) return product : : direct_exp ( v ) ;
2019-08-24 16:14:38 +00:00
ld d = hypot_d ( GDIM , v ) ;
if ( d > 0 ) for ( int i = 0 ; i < GDIM ; i + + ) v [ i ] = v [ i ] * sin_auto ( d ) / d ;
2019-08-25 18:08:35 +00:00
v [ LDIM ] = cos_auto ( d ) ;
2019-08-24 16:14:38 +00:00
return v ;
}
# if HDR
2020-04-11 13:08:24 +00:00
constexpr flagtype pfNO_INTERPOLATION = 1 ; /**< in tables (sol/nih geometries), do not use interpolations */
constexpr flagtype pfNO_DISTANCE = 2 ; /**< we just need the directions -- this makes it a bit faster in sol/nih geometries */
constexpr flagtype pfLOW_BS_ITER = 4 ; /**< low iterations in binary search (nil geometry, sl2 not affected currently) */
constexpr flagtype pQUICK = pfNO_INTERPOLATION | pfLOW_BS_ITER ;
constexpr flagtype pNORMAL = 0 ;
2019-08-24 16:14:38 +00:00
# endif
/** inverse exponential function \see hr::direct_exp */
2020-07-27 16:49:04 +00:00
EX hyperpoint inverse_exp ( const shiftpoint h , flagtype prec IS ( pNORMAL ) ) {
2019-09-13 17:50:12 +00:00
# if CAP_SOLV
2019-12-14 11:28:45 +00:00
if ( sn : : in ( ) ) {
2021-10-07 10:40:52 +00:00
/* this will be more precise for use in set_view in intra */
if ( sqhypot_d ( 3 , h . h ) < 2e-9 ) return h . h - C0 ;
2019-10-03 18:10:48 +00:00
if ( nih )
2020-07-27 16:49:04 +00:00
return sn : : get_inverse_exp_nsym ( h . h , prec ) ;
2019-10-03 18:10:48 +00:00
else
2020-07-27 16:49:04 +00:00
return sn : : get_inverse_exp_symsol ( h . h , prec ) ;
2019-10-03 18:10:48 +00:00
}
2019-09-13 17:50:12 +00:00
# endif
2020-07-27 16:49:04 +00:00
if ( nil ) return nilv : : get_inverse_exp ( h . h , prec ) ;
2019-08-24 16:14:38 +00:00
if ( sl2 ) return slr : : get_inverse_exp ( h ) ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) return product : : inverse_exp ( h . h ) ;
2019-08-24 16:14:38 +00:00
ld d = acos_auto_clamp ( h [ GDIM ] ) ;
2020-07-03 12:42:56 +00:00
hyperpoint v = Hypc ;
2019-08-24 16:14:38 +00:00
if ( d & & sin_auto ( d ) ) for ( int i = 0 ; i < GDIM ; i + + ) v [ i ] = h [ i ] * d / sin_auto ( d ) ;
v [ 3 ] = 0 ;
return v ;
}
2020-04-11 13:08:24 +00:00
EX ld geo_dist ( const hyperpoint h1 , const hyperpoint h2 , flagtype prec IS ( pNORMAL ) ) {
2019-08-24 16:14:38 +00:00
if ( ! nonisotropic ) return hdist ( h1 , h2 ) ;
2020-09-16 03:57:05 +00:00
return hypot_d ( 3 , inverse_exp ( shiftless ( nisot : : translate ( h1 , - 1 ) * h2 , prec ) ) ) ;
2020-07-27 16:49:04 +00:00
}
EX ld geo_dist ( const shiftpoint h1 , const shiftpoint h2 , flagtype prec IS ( pNORMAL ) ) {
if ( ! nonisotropic ) return hdist ( h1 , h2 ) ;
2020-09-16 03:57:05 +00:00
return hypot_d ( 3 , inverse_exp ( shiftless ( nisot : : translate ( h1 . h , - 1 ) * h2 . h , h2 . shift - h1 . shift ) , prec ) ) ;
2019-08-24 16:14:38 +00:00
}
2020-04-14 14:42:11 +00:00
EX ld geo_dist_q ( const hyperpoint h1 , const hyperpoint h2 , flagtype prec IS ( pNORMAL ) ) {
auto d = geo_dist ( h1 , h2 , prec ) ;
2022-11-12 21:38:45 +00:00
if ( elliptic & & d > 90. _deg ) return M_PI - d ;
2020-04-14 14:42:11 +00:00
return d ;
}
2019-09-05 10:00:55 +00:00
EX hyperpoint lp_iapply ( const hyperpoint h ) {
2019-11-14 16:20:55 +00:00
return nisot : : local_perspective_used ( ) ? inverse ( NLP ) * h : h ;
2019-08-24 16:14:38 +00:00
}
2019-09-05 10:00:55 +00:00
EX hyperpoint lp_apply ( const hyperpoint h ) {
2019-11-14 16:20:55 +00:00
return nisot : : local_perspective_used ( ) ? NLP * h : h ;
2019-08-24 16:14:38 +00:00
}
2022-12-13 22:51:28 +00:00
EX hyperpoint smalltangent ( ) { return xtangent ( .1 ) ; }
2019-08-24 16:14:38 +00:00
2019-10-21 20:43:03 +00:00
EX void cyclefix ( ld & a , ld b ) {
2022-11-12 21:38:45 +00:00
while ( a > b + M_PI ) a - = TAU ;
while ( a < b - M_PI ) a + = TAU ;
2019-10-21 20:43:03 +00:00
}
EX ld raddif ( ld a , ld b ) {
ld d = a - b ;
if ( d < 0 ) d = - d ;
2022-11-12 21:38:45 +00:00
if ( d > TAU ) d - = TAU ;
if ( d > M_PI ) d = TAU - d ;
2019-10-21 20:43:03 +00:00
return d ;
}
2020-04-06 06:39:31 +00:00
EX unsigned bucketer ( ld x ) {
return unsigned ( ( long long ) ( x * 10000 + 100000.5 ) - 100000 ) ;
2019-11-03 13:19:11 +00:00
}
2020-04-06 06:39:31 +00:00
EX unsigned bucketer ( hyperpoint h ) {
unsigned dx = 0 ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) {
2020-02-07 18:45:07 +00:00
auto d = product_decompose ( h ) ;
h = d . second ;
dx + = bucketer ( d . first ) * 50 ;
}
dx + = bucketer ( h [ 0 ] ) + 1000 * bucketer ( h [ 1 ] ) + 1000000 * bucketer ( h [ 2 ] ) ;
if ( MDIM = = 4 ) dx + = bucketer ( h [ 3 ] ) * 1000000001 ;
2021-08-04 13:18:22 +00:00
if ( elliptic ) dx = min ( dx , - dx ) ;
2020-02-07 18:45:07 +00:00
return dx ;
2019-11-03 13:19:11 +00:00
}
2019-10-21 20:43:03 +00:00
2021-03-30 09:27:48 +00:00
# if MAXMDIM >= 4
2020-06-03 14:42:35 +00:00
/** @brief project the origin to the triangle [h1,h2,h3] */
EX hyperpoint project_on_triangle ( hyperpoint h1 , hyperpoint h2 , hyperpoint h3 ) {
h1 / = h1 [ 3 ] ;
h2 / = h2 [ 3 ] ;
h3 / = h3 [ 3 ] ;
transmatrix T ;
T [ 0 ] = h1 ; T [ 1 ] = h2 ; T [ 2 ] = h3 ;
T [ 3 ] = C0 ;
2022-08-05 17:20:58 +00:00
ld det_orig = det3 ( T ) ;
2020-06-03 14:42:35 +00:00
hyperpoint orthogonal = ( h2 - h1 ) ^ ( h3 - h1 ) ;
T [ 0 ] = orthogonal ; T [ 1 ] = h2 - h1 ; T [ 2 ] = h3 - h1 ;
2022-08-05 17:20:58 +00:00
ld det_orth = det3 ( T ) ;
2020-06-03 14:42:35 +00:00
hyperpoint result = orthogonal * ( det_orig / det_orth ) ;
result [ 3 ] = 1 ;
return normalize ( result ) ;
}
2021-03-30 09:27:48 +00:00
# endif
2020-06-03 14:42:35 +00:00
2020-03-29 10:01:55 +00:00
EX hyperpoint lerp ( hyperpoint a0 , hyperpoint a1 , ld x ) {
return a0 + ( a1 - a0 ) * x ;
}
2020-11-01 10:32:22 +00:00
EX hyperpoint linecross ( hyperpoint a , hyperpoint b , hyperpoint c , hyperpoint d ) {
a / = a [ LDIM ] ;
b / = b [ LDIM ] ;
c / = c [ LDIM ] ;
d / = d [ LDIM ] ;
ld bax = b [ 0 ] - a [ 0 ] ;
ld dcx = d [ 0 ] - c [ 0 ] ;
ld cax = c [ 0 ] - a [ 0 ] ;
ld bay = b [ 1 ] - a [ 1 ] ;
ld dcy = d [ 1 ] - c [ 1 ] ;
ld cay = c [ 1 ] - a [ 1 ] ;
hyperpoint res ;
res [ 0 ] = ( cay * dcx * bax + a [ 0 ] * bay * dcx - c [ 0 ] * dcy * bax ) / ( bay * dcx - dcy * bax ) ;
res [ 1 ] = ( cax * dcy * bay + a [ 1 ] * bax * dcy - c [ 1 ] * dcx * bay ) / ( bax * dcy - dcx * bay ) ;
2021-05-12 18:14:33 +00:00
res [ 2 ] = 0 ;
res [ 3 ] = 0 ;
res [ GDIM ] = 1 ;
2020-11-01 10:32:22 +00:00
return normalize ( res ) ;
}
2021-07-08 18:53:07 +00:00
EX ld inner2 ( hyperpoint h1 , hyperpoint h2 ) {
return
hyperbolic ? h1 [ LDIM ] * h2 [ LDIM ] - h1 [ 0 ] * h2 [ 0 ] - h1 [ 1 ] * h2 [ 1 ] :
sphere ? h1 [ LDIM ] * h2 [ LDIM ] + h1 [ 0 ] * h2 [ 0 ] + h1 [ 1 ] * h2 [ 1 ] :
h1 [ 0 ] * h2 [ 0 ] + h1 [ 1 ] * h2 [ 1 ] ;
}
EX hyperpoint circumscribe ( hyperpoint a , hyperpoint b , hyperpoint c ) {
hyperpoint h = C0 ;
b = b - a ;
c = c - a ;
if ( euclid ) {
ld b2 = inner2 ( b , b ) / 2 ;
ld c2 = inner2 ( c , c ) / 2 ;
ld det = c [ 1 ] * b [ 0 ] - b [ 1 ] * c [ 0 ] ;
h = a ;
h [ 1 ] + = ( c2 * b [ 0 ] - b2 * c [ 0 ] ) / det ;
h [ 0 ] + = ( c2 * b [ 1 ] - b2 * c [ 1 ] ) / - det ;
return h ;
}
if ( inner2 ( b , b ) < 0 ) {
b = b / sqrt ( - inner2 ( b , b ) ) ;
c = c + b * inner2 ( c , b ) ;
h = h + b * inner2 ( h , b ) ;
}
else {
b = b / sqrt ( inner2 ( b , b ) ) ;
c = c - b * inner2 ( c , b ) ;
h = h - b * inner2 ( h , b ) ;
}
if ( inner2 ( c , c ) < 0 ) {
c = c / sqrt ( - inner2 ( c , c ) ) ;
h = h + c * inner2 ( h , c ) ;
}
else {
c = c / sqrt ( inner2 ( c , c ) ) ;
h = h - c * inner2 ( h , c ) ;
}
if ( h [ LDIM ] < 0 ) h [ 0 ] = - h [ 0 ] , h [ 1 ] = - h [ 1 ] , h [ LDIM ] = - h [ LDIM ] ;
ld i = inner2 ( h , h ) ;
if ( i > 0 ) h / = sqrt ( i ) ;
else h / = - sqrt ( - i ) ;
return h ;
}
2021-07-08 18:58:37 +00:00
EX ld inner3 ( hyperpoint h1 , hyperpoint h2 ) {
return
hyperbolic ? h1 [ LDIM ] * h2 [ LDIM ] - h1 [ 0 ] * h2 [ 0 ] - h1 [ 1 ] * h2 [ 1 ] - h1 [ 2 ] * h2 [ 2 ] :
sphere ? h1 [ LDIM ] * h2 [ LDIM ] + h1 [ 0 ] * h2 [ 0 ] + h1 [ 1 ] * h2 [ 1 ] + h1 [ 2 ] * h2 [ 2 ] :
h1 [ 0 ] * h2 [ 0 ] + h1 [ 1 ] * h2 [ 1 ] ;
}
/** circumscribe for H3 and S3 (not for E3 yet!) */
EX hyperpoint circumscribe ( hyperpoint a , hyperpoint b , hyperpoint c , hyperpoint d ) {
array < hyperpoint , 4 > ds = { b - a , c - a , d - a , C0 } ;
for ( int i = 0 ; i < 3 ; i + + ) {
if ( inner3 ( ds [ i ] , ds [ i ] ) < 0 ) {
ds [ i ] = ds [ i ] / sqrt ( - inner3 ( ds [ i ] , ds [ i ] ) ) ;
for ( int j = i + 1 ; j < 4 ; j + + )
ds [ j ] = ds [ j ] + ds [ i ] * inner3 ( ds [ i ] , ds [ j ] ) ;
}
else {
ds [ i ] = ds [ i ] / sqrt ( inner3 ( ds [ i ] , ds [ i ] ) ) ;
for ( int j = i + 1 ; j < 4 ; j + + )
ds [ j ] = ds [ j ] - ds [ i ] * inner3 ( ds [ i ] , ds [ j ] ) ;
}
}
hyperpoint & h = ds [ 3 ] ;
if ( h [ 3 ] < 0 ) h = - h ;
ld i = inner3 ( h , h ) ;
if ( i > 0 ) h / = sqrt ( i ) ;
else h / = - sqrt ( - i ) ;
return h ;
}
2021-07-08 18:53:07 +00:00
2022-04-22 22:52:58 +00:00
/** the point in distance dist from 'material' to 'dir' (usually an (ultra)ideal point) */
EX hyperpoint towards_inf ( hyperpoint material , hyperpoint dir , ld dist IS ( 1 ) ) {
2022-04-22 22:42:53 +00:00
transmatrix T = gpushxto0 ( material ) ;
2022-04-22 22:52:58 +00:00
hyperpoint id = T * dir ;
2022-04-24 22:07:37 +00:00
return rgpushxto0 ( material ) * rspintox ( id ) * xpush0 ( dist ) ;
2022-04-22 22:42:53 +00:00
}
2021-07-08 18:53:07 +00:00
EX bool clockwise ( hyperpoint h1 , hyperpoint h2 ) {
return h1 [ 0 ] * h2 [ 1 ] > h1 [ 1 ] * h2 [ 0 ] ;
}
2021-08-22 21:41:38 +00:00
EX ld worst_precision_error ;
# if HDR
struct hr_precision_error : hr_exception { hr_precision_error ( ) : hr_exception ( " precision error " ) { } } ;
# endif
/** check if a and b are the same, testing for equality. Throw an exception or warning if not sure */
EX bool same_point_may_warn ( hyperpoint a , hyperpoint b ) {
ld d = hdist ( a , b ) ;
if ( d > 1e-2 ) return false ;
if ( d > 1e-3 ) throw hr_precision_error ( ) ;
if ( d > 1e-6 & & worst_precision_error < = 1e-6 )
addMessage ( " warning: precision errors are building up! " ) ;
if ( d > worst_precision_error ) worst_precision_error = d ;
return true ;
}
2021-07-08 18:53:07 +00:00
2018-06-10 23:58:31 +00:00
}