2018-02-08 23:40:26 +00:00
// Hyperbolic Rogue -- hyperbolic graphics
2019-08-10 11:43:24 +00:00
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
/** \file hypgraph.cpp
* \ brief mapping hyperpoints to the screen , and related functions
*/
2018-02-08 23:40:26 +00:00
2019-09-05 07:15:40 +00:00
# include "hyper.h"
2018-06-10 23:58:31 +00:00
namespace hr {
2020-07-27 16:49:04 +00:00
hyperpoint ghxy , ghgxy ;
2020-09-16 23:55:22 +00:00
shiftpoint ghpm = shiftless ( C02 ) ;
2017-07-10 18:47:38 +00:00
2022-12-04 13:48:12 +00:00
EX ld flip_limit = 1.1 ;
2022-12-08 18:38:06 +00:00
EX bool flip_sphere ( ) { return GDIM = = 2 & & sphere & & pconf . alpha > flip_limit ; }
2022-12-04 13:48:12 +00:00
EX bool sphere_flipped ;
2019-08-09 21:39:36 +00:00
2020-07-27 16:49:04 +00:00
void ghcheck ( hyperpoint & ret , const shiftpoint & H ) {
2020-04-19 21:22:41 +00:00
if ( hypot_d ( 2 , ret - ghxy ) < hypot_d ( 2 , ghgxy - ghxy ) ) {
ghpm = H ; ghgxy = ret ;
2017-07-10 18:47:38 +00:00
}
}
2019-09-06 06:17:02 +00:00
EX void camrotate ( ld & hx , ld & hy ) {
2023-08-14 16:08:28 +00:00
hyperpoint p = hyperpoint ( hx , hy , 1 , 1 ) ;
p = pconf . cam ( ) * p ;
hx = p [ 0 ] / p [ 2 ] , hy = p [ 1 ] / p [ 2 ] ;
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool non_spatial_model ( ) {
2018-12-05 18:57:35 +00:00
if ( among ( pmodel , mdRotatedHyperboles , mdJoukowsky , mdJoukowskyInverted , mdPolygonal , mdPolynomial ) )
return true ;
if ( pmodel = = mdSpiral & & euclid )
return true ;
2020-07-03 12:42:33 +00:00
# if CAP_GL
2019-10-21 20:34:20 +00:00
return pmodel & & vid . consider_shader_projection & & ( get_shader_flags ( ) & SF_DIRECT ) ;
2020-07-03 12:42:33 +00:00
# else
return false ;
# endif
2018-12-05 18:57:35 +00:00
}
2020-04-16 22:53:58 +00:00
EX hyperpoint perspective_to_space ( hyperpoint h , ld alpha IS ( pconf . alpha ) , eGeometryClass gc IS ( ginf [ geometry ] . cclass ) ) {
2018-11-06 14:53:50 +00:00
ld hx = h [ 0 ] , hy = h [ 1 ] ;
2017-07-10 18:47:38 +00:00
2018-11-06 14:53:50 +00:00
if ( gc = = gcEuclid )
2019-02-22 19:58:40 +00:00
return hpxy ( hx * ( 1 + alpha ) , hy * ( 1 + alpha ) ) ;
2018-02-20 10:15:08 +00:00
2017-07-10 18:47:38 +00:00
ld hr = hx * hx + hy * hy ;
2020-04-05 08:55:01 +00:00
if ( LDIM = = 3 ) hr + = h [ 2 ] * h [ 2 ] ;
2017-07-10 18:47:38 +00:00
2018-11-06 14:53:50 +00:00
if ( hr > .9999 & & gc = = gcHyperbolic ) return Hypc ;
2017-07-10 18:47:38 +00:00
ld A , B , C ;
2018-11-06 14:53:50 +00:00
ld curv = gc = = gcSphere ? 1 : - 1 ;
2017-07-10 18:47:38 +00:00
A = 1 + curv * hr ;
2020-01-02 15:56:01 +00:00
B = 2 * hr * alpha * - curv ;
C = 1 - curv * hr * alpha * alpha ;
2017-07-10 18:47:38 +00:00
B / = A ; C / = A ;
ld rootsign = 1 ;
2021-02-01 10:45:13 +00:00
if ( gc = = gcSphere & & pconf . alpha > 1 ) rootsign = - 1 ;
2017-07-10 18:47:38 +00:00
ld hz = B / 2 + rootsign * sqrt ( C + B * B / 4 ) ;
hyperpoint H ;
2020-01-02 15:56:01 +00:00
H [ 0 ] = hx * ( hz + alpha ) ;
H [ 1 ] = hy * ( hz + alpha ) ;
2020-04-05 08:55:01 +00:00
if ( LDIM = = 3 ) H [ 2 ] = h [ 2 ] * ( hz + alpha ) ;
2019-08-17 21:28:41 +00:00
H [ LDIM ] = hz ;
2017-07-10 18:47:38 +00:00
2018-11-06 14:53:50 +00:00
return H ;
}
2020-04-16 22:53:58 +00:00
EX hyperpoint space_to_perspective ( hyperpoint z , ld alpha IS ( pconf . alpha ) ) {
2019-08-17 21:28:41 +00:00
ld s = 1 / ( alpha + z [ LDIM ] ) ;
2018-11-06 14:53:50 +00:00
z [ 0 ] * = s ;
z [ 1 ] * = s ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2019-03-20 01:10:53 +00:00
z [ 2 ] * = s ;
z [ 3 ] = 0 ;
}
else
z [ 2 ] = 0 ;
2018-11-06 14:53:50 +00:00
return z ;
}
2020-12-30 03:13:47 +00:00
EX hyperpoint pointable ( ) {
return WDIM = = 2 & & GDIM = = 3 ? zpush0 ( cgi . FLOOR ) : C0 ;
}
2020-12-30 02:26:46 +00:00
2020-12-30 03:13:47 +00:00
/** find a shiftpoint which minimizes value -- we represent points by matrices to make things a bit simpler */
2020-12-30 02:26:46 +00:00
2020-12-30 03:13:47 +00:00
EX shiftmatrix minimize_point_value ( shiftmatrix T , function < ld ( const shiftmatrix & ) > value ) {
ld best = value ( T ) ;
2020-12-30 02:26:46 +00:00
for ( int it = 0 ; it < 50 ; it + + )
2020-12-30 19:21:07 +00:00
for ( int s = 0 ; s < 2 * WDIM ; s + + ) {
shiftmatrix T1 = T * cpush ( s / 2 , ( s & 1 ? 1 : - 1 ) * pow ( 1.2 , - it ) ) ;
2020-12-30 03:13:47 +00:00
ld dist = value ( T1 ) ;
2020-12-30 02:26:46 +00:00
if ( dist < best ) best = dist , T = T1 ;
if ( mdBandAny ( ) ) {
2022-11-12 21:38:45 +00:00
T1 . shift + = TAU ;
2020-12-30 03:13:47 +00:00
dist = value ( T1 ) ;
2020-12-30 02:26:46 +00:00
if ( dist < best ) best = dist , T = T1 ;
2022-11-12 21:38:45 +00:00
T1 . shift - = 720. _deg ;
2020-12-30 03:13:47 +00:00
dist = value ( T1 ) ;
2020-12-30 02:26:46 +00:00
if ( dist < best ) best = dist , T = T1 ;
2022-11-12 21:38:45 +00:00
T1 . shift + = TAU ;
2020-12-30 02:26:46 +00:00
}
}
2020-12-30 03:13:47 +00:00
return T ;
}
EX shiftpoint find_on_screen ( hyperpoint hxy , const shiftmatrix & T ) {
hyperpoint rel = pointable ( ) ;
auto distance_at = [ & ] ( const shiftmatrix & T1 ) {
hyperpoint h1 ;
applymodel ( T1 * rel , h1 ) ;
return sqhypot_d ( 2 , hxy - h1 ) ;
} ;
return minimize_point_value ( T , distance_at ) * rel ;
2020-12-30 02:26:46 +00:00
}
2020-07-27 16:49:04 +00:00
EX shiftpoint gethyper ( ld x , ld y ) {
2018-11-06 14:53:50 +00:00
2018-11-17 18:24:02 +00:00
ld hx = ( x - current_display - > xcenter ) / current_display - > radius ;
2020-04-16 22:53:58 +00:00
ld hy = ( y - current_display - > ycenter ) / current_display - > radius / pconf . stretch ;
2020-04-19 21:22:41 +00:00
hyperpoint hxy = point3 ( hx , hy , 0 ) ;
2018-11-06 14:53:50 +00:00
2020-12-30 02:26:46 +00:00
if ( WDIM = = 2 & & GDIM = = 3 ) {
2020-12-30 03:13:47 +00:00
return mouseover ? find_on_screen ( hxy , ggmatrix ( mouseover ) ) : shiftless ( Hypc ) ;
2020-12-30 02:26:46 +00:00
}
2020-12-30 03:13:47 +00:00
2018-11-06 14:53:50 +00:00
if ( pmodel ) {
2020-04-19 21:22:41 +00:00
ghxy = hxy ;
2020-12-30 03:13:47 +00:00
return find_on_screen ( hxy , rgpushxto0 ( ghpm ) ) ;
2018-11-06 14:53:50 +00:00
}
2023-08-14 16:08:28 +00:00
if ( ! models : : camera_straight ) camrotate ( hx , hy ) ;
2018-11-06 14:53:50 +00:00
2020-07-27 16:49:04 +00:00
return shiftless ( perspective_to_space ( hpxyz ( hx , hy , 0 ) ) ) ;
2017-07-10 18:47:38 +00:00
}
void ballmodel ( hyperpoint & ret , double alpha , double d , double zl ) {
2019-05-29 14:27:24 +00:00
hyperpoint H = ypush ( vid . camera ) * xpush ( d ) * ypush ( zl ) * C0 ;
2020-04-16 22:53:58 +00:00
ld tzh = pconf . ballproj + H [ LDIM ] ;
2017-07-10 18:47:38 +00:00
ld ax = H [ 0 ] / tzh ;
ld ay = H [ 1 ] / tzh ;
ld ca = cos ( alpha ) , sa = sin ( alpha ) ;
2018-10-25 17:58:38 +00:00
2017-07-10 18:47:38 +00:00
ret [ 0 ] = ax * ca ;
2018-10-25 17:58:38 +00:00
ret [ 1 ] = ay ;
ret [ 2 ] = ax * sa ;
2023-08-14 14:18:44 +00:00
ret = pconf . ball ( ) * ret ;
2017-07-10 18:47:38 +00:00
}
2020-12-27 16:14:50 +00:00
bool use_z_coordinate ( ) {
2020-12-27 16:37:39 +00:00
# if CAP_VR
2020-12-30 13:20:30 +00:00
if ( vrhr : : rendering ( ) ) return true ;
2020-12-27 16:37:39 +00:00
# endif
2024-01-07 08:07:54 +00:00
return current_display - > separate_eyes ( ) ;
2020-12-27 16:14:50 +00:00
}
2018-02-03 12:41:49 +00:00
void apply_depth ( hyperpoint & f , ld z ) {
if ( vid . usingGL )
2020-12-27 16:14:50 +00:00
f [ 2 ] = z * pconf . depth_scaling ;
2018-02-03 12:41:49 +00:00
else {
2020-12-27 16:14:50 +00:00
z = z * current_display - > radius * pconf . depth_scaling ;
2019-10-21 20:34:20 +00:00
ld mul = current_display - > radius / ( current_display - > radius + z ) ;
2018-02-03 12:41:49 +00:00
f [ 0 ] = f [ 0 ] * mul ;
f [ 1 ] = f [ 1 ] * mul ;
2018-11-17 18:24:02 +00:00
f [ 2 ] = vid . xres * current_display - > eyewidth ( ) / 2 / current_display - > radius + vid . ipd * mul / 2 ;
2018-02-03 12:41:49 +00:00
}
}
2018-11-06 14:53:50 +00:00
bool hypot_zlev ( ld zlev , ld & d , ld & df , ld & zf ) {
if ( zlev = = 1 ) {
2018-03-27 02:01:30 +00:00
df = 1 ; zf = 0 ;
return false ;
}
else {
// (0,0,1) -> (0, sin z, cos z) -> (sin d cos z, sin z, cos d cos z)
ld z = geom3 : : factor_to_lev ( zlev ) ;
2018-11-06 14:53:50 +00:00
2018-03-27 02:01:30 +00:00
ld tz = sin_auto ( z ) ;
ld td = sin_auto ( abs ( d ) ) * cos_auto ( z ) ;
ld h = hypot ( td , tz ) ;
2018-11-06 14:53:50 +00:00
zf = tz / h , df = td / h ;
2018-03-27 02:01:30 +00:00
if ( d > 0 )
d = hypot_auto ( d , z ) ;
2020-12-27 16:15:05 +00:00
else {
d = - hypot_auto ( - d , z ) ;
zf = - zf ;
}
2018-03-27 02:01:30 +00:00
return true ;
}
}
2018-04-21 10:18:33 +00:00
int twopoint_sphere_flips ;
bool twopoint_do_flips ;
2018-03-27 02:01:30 +00:00
2023-03-16 14:18:02 +00:00
EX ld find_zlev ( hyperpoint & H ) {
2018-11-06 14:53:50 +00:00
2018-12-05 18:57:35 +00:00
if ( spatial_graphics ) {
2018-11-06 14:53:50 +00:00
ld zlev = zlevel ( H ) ;
2022-02-26 08:51:58 +00:00
if ( zlev > 1 - 1e-9 & & zlev < 1 + 1e-9 ) return 1 ;
2018-11-06 14:53:50 +00:00
H / = zlev ;
return zlev ;
}
2017-07-10 18:47:38 +00:00
2018-11-06 14:53:50 +00:00
return 1 ;
}
ld get_tz ( hyperpoint H ) {
2020-04-16 22:53:58 +00:00
ld tz = pconf . alpha + H [ LDIM ] ;
2017-11-03 18:20:54 +00:00
if ( tz < BEHIND_LIMIT & & tz > - BEHIND_LIMIT ) tz = BEHIND_LIMIT ;
2018-11-06 14:53:50 +00:00
return tz ;
}
2019-08-09 19:00:52 +00:00
EX ld atan2 ( hyperpoint h ) {
2018-11-06 14:53:50 +00:00
return atan2 ( h [ 1 ] , h [ 0 ] ) ;
}
2019-03-20 01:10:53 +00:00
pair < ld , ld > move_z_to_y ( hyperpoint & H ) {
2019-08-15 13:05:43 +00:00
if ( GDIM = = 2 ) return make_pair ( 0 , 0 ) ;
2019-03-20 01:10:53 +00:00
ld R = hypot ( H [ 1 ] , H [ 2 ] ) ;
pair < ld , ld > res = { H [ 1 ] / R , H [ 2 ] / R } ;
H [ 1 ] = R ; H [ 2 ] = 0 ;
return res ;
}
void move_y_to_z ( hyperpoint & H , pair < ld , ld > coef ) {
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2019-03-20 01:10:53 +00:00
H [ 2 ] = H [ 1 ] * coef . second ;
H [ 1 ] = H [ 1 ] * coef . first ;
2021-03-30 09:27:48 +00:00
# if MAXMDIM >= 4
2019-03-20 01:10:53 +00:00
H [ 3 ] = 1 ;
2021-03-30 09:27:48 +00:00
# endif
2019-03-20 01:10:53 +00:00
}
}
2020-07-27 16:49:04 +00:00
template < class T > void makeband ( shiftpoint H , hyperpoint & ret , const T & f ) {
ld zlev = find_zlev ( H . h ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H . h ) ;
2020-07-27 16:49:04 +00:00
auto r = move_z_to_y ( H . h ) ;
2017-07-10 18:47:38 +00:00
2018-11-06 14:53:50 +00:00
ld x , y , yf , zf = 0 ;
y = asin_auto ( H [ 1 ] ) ;
2019-04-03 18:30:35 +00:00
x = asin_auto_clamp ( H [ 0 ] / cos_auto ( y ) ) ;
2018-11-06 14:53:50 +00:00
if ( sphere ) {
2019-08-17 21:28:41 +00:00
if ( H [ LDIM ] < 0 & & x > 0 ) x = M_PI - x ;
else if ( H [ LDIM ] < 0 & & x < = 0 ) x = - M_PI - x ;
2017-07-10 18:47:38 +00:00
}
2020-07-27 16:49:04 +00:00
x + = H . shift ;
2018-11-06 14:53:50 +00:00
hypot_zlev ( zlev , y , yf , zf ) ;
2017-07-10 18:47:38 +00:00
2018-11-06 14:53:50 +00:00
f ( x , y ) ;
2017-07-10 18:47:38 +00:00
2018-11-06 14:53:50 +00:00
ld yzf = y * zf ; y * = yf ;
2019-02-22 19:58:40 +00:00
ret = hpxyz ( x / M_PI , y / M_PI , 0 ) ;
2019-03-20 01:10:53 +00:00
move_y_to_z ( ret , r ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2020-12-27 16:14:50 +00:00
if ( zlev ! = 1 & & use_z_coordinate ( ) )
2018-11-06 14:53:50 +00:00
apply_depth ( ret , yzf / M_PI ) ;
return ;
}
2018-10-23 18:08:57 +00:00
2022-03-27 07:04:21 +00:00
EX void makeband_f ( shiftpoint H , hyperpoint & ret , const hr : : function < void ( ld & , ld & ) > & f ) {
makeband ( H , ret , f ) ;
}
2018-11-06 14:53:50 +00:00
void band_conformal ( ld & x , ld & y ) {
switch ( cgclass ) {
case gcSphere :
y = atanh ( sin ( y ) ) ;
x * = 2 ; y * = 2 ;
break ;
case gcHyperbolic :
y = 2 * atan ( tanh ( y / 2 ) ) ;
x * = 2 ; y * = 2 ;
break ;
case gcEuclid :
2019-07-23 13:08:07 +00:00
default :
2018-11-06 14:53:50 +00:00
// y = y;
y * = 2 ; x * = 2 ;
break ;
2018-10-23 18:08:57 +00:00
}
2018-11-06 14:53:50 +00:00
}
2018-10-23 18:08:57 +00:00
2018-11-06 14:53:50 +00:00
void make_twopoint ( ld & x , ld & y ) {
2020-04-16 22:53:58 +00:00
auto p = pconf . twopoint_param ;
2018-11-06 14:53:50 +00:00
ld dleft = hypot_auto ( x - p , y ) ;
ld dright = hypot_auto ( x + p , y ) ;
if ( sphere ) {
int tss = twopoint_sphere_flips ;
if ( tss & 1 ) { tss - - ;
2022-11-12 21:38:45 +00:00
dleft = TAU - 2 * p - dleft ;
dright = TAU - 2 * p - dright ;
2018-11-06 14:53:50 +00:00
swap ( dleft , dright ) ;
y = - y ;
2018-10-23 18:08:57 +00:00
}
2018-11-06 14:53:50 +00:00
while ( tss ) { tss - = 2 ;
2022-11-12 21:38:45 +00:00
dleft = TAU - 4 * p + dleft ;
dright = TAU - 4 * p + dright ;
2017-07-10 18:47:38 +00:00
}
2018-04-18 18:52:17 +00:00
}
2018-11-06 14:53:50 +00:00
x = ( dright * dright - dleft * dleft ) / 4 / p ;
y = ( y > 0 ? 1 : - 1 ) * sqrt ( dleft * dleft - ( x - p ) * ( x - p ) + 1e-9 ) ;
}
2018-04-18 18:52:17 +00:00
2018-11-07 06:20:36 +00:00
hyperpoint mobius ( hyperpoint h , ld angle , ld scale = 1 ) {
h = perspective_to_space ( h * scale , 1 , gcSphere ) ;
2019-08-09 21:59:32 +00:00
h = cspin ( 1 , 2 , angle * degree ) * h ;
2018-11-07 06:20:36 +00:00
return space_to_perspective ( h , 1 ) / scale ;
}
2019-07-12 21:10:01 +00:00
hyperpoint compute_hybrid ( hyperpoint H , int rootid ) {
2020-04-16 22:53:58 +00:00
auto & t = pconf . twopoint_param ;
2019-07-12 21:10:01 +00:00
hyperpoint Hl = xpush ( + t ) * H ;
hyperpoint Hr = xpush ( - t ) * H ;
ld g = ( Hl [ 0 ] + 1e-7 ) / ( Hl [ 1 ] + 1e-8 ) ;
ld d = hdist0 ( Hr ) ;
hyperpoint spinned = spintox ( Hl ) * xpush0 ( 2 * t ) ;
if ( Hl [ 0 ] < 0 ) spinned = pispin * spinned ;
ld y = asin_auto ( spinned [ 1 ] ) ;
ld x = asin_auto_clamp ( spinned [ 0 ] / cos_auto ( y ) ) ;
int sign = ( Hl [ 0 ] > 0 ? 1 : - 1 ) * hdist0 ( Hl ) < x ? - 1 : 1 ;
switch ( rootid & 3 ) {
case 1 : sign = - sign ; break ;
case 2 : sign = 1 ; break ;
case 3 : sign = - 1 ; break ;
}
// (x + t) / g = y
// yy + (x-t)(x-t) = dd
// (x+t)*(x+t)/g*g + x*x + t*t - 2*x*t = dd
// x*x*(1+1/g*g) + t*t*(1+1/g*g) + 2xt (1/gg-1) = dd
// xx + 2xt (1/gg-1) / (1+1/gg) = dd / (1+1/gg) - tt
ld b = t * ( 1 / g / g - 1 ) / ( 1 + 1 / g / g ) ;
ld c = d * d / ( 1 + 1 / g / g ) - t * t ;
// xx + 2bx = c
// xx + 2bx + bb = c + bb
// (x+b)^2 = c+bb
// x = +/- sqrt(c+bb) - b
ld a = c + b * b ;
hyperpoint ret ;
ret [ 0 ] = ( a > 0 ? sign * sqrt ( a ) : 0 ) - b ;
ret [ 1 ] = ( ret [ 0 ] + t ) / g ;
ret [ 2 ] = 0 ;
return ret ;
}
2020-02-23 01:51:27 +00:00
EX ld signed_sqrt ( ld x ) { return x > 0 ? sqrt ( x ) : - sqrt ( - x ) ; }
2019-08-14 16:59:21 +00:00
2020-08-20 14:02:34 +00:00
EX int axial_x , axial_y ;
2023-05-15 00:25:13 +00:00
/** in perspective projections, compute inverse_exp (or similar) on CPU, but perspective on GPU (needs consider_shader_projection off) */
EX bool semidirect_rendering = false ;
/** flag for semidirect_rendering */
EX bool computing_semidirect = false ;
2020-09-11 09:13:18 +00:00
EX void apply_perspective ( const hyperpoint & H , hyperpoint & ret ) {
2023-05-15 00:25:13 +00:00
if ( computing_semidirect ) { ret = H ; ret [ 3 ] = 1 ; return ; }
2022-12-29 19:50:47 +00:00
if ( H [ 2 ] = = 0 ) { ret [ 0 ] = 1e6 ; ret [ 1 ] = 1e6 ; ret [ 2 ] = 0 ; return ; }
2020-09-11 09:13:18 +00:00
ld ratio = vid . xres / current_display - > tanfov / current_display - > radius / 2 ;
ret [ 0 ] = H [ 0 ] / H [ 2 ] * ratio ;
ret [ 1 ] = H [ 1 ] / H [ 2 ] * ratio ;
2022-12-29 19:50:47 +00:00
ret [ 2 ] = H [ 2 ] ;
2020-09-11 09:13:18 +00:00
ret [ 3 ] = 1 ;
}
EX void apply_nil_rotation ( hyperpoint & H ) {
if ( nil ) {
2023-01-29 23:02:13 +00:00
nilv : : convert_ref ( H , nilv : : model_used , nilv : : nmSym ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2023-01-29 23:02:13 +00:00
nilv : : convert_ref ( H , nilv : : nmSym , pconf . rotational_nil ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( H ) ;
2020-09-11 09:13:18 +00:00
}
}
2020-07-27 16:49:04 +00:00
EX void applymodel ( shiftpoint H_orig , hyperpoint & ret ) {
2020-09-15 17:17:07 +00:00
apply_other_model ( H_orig , ret , pmodel ) ;
}
2020-12-29 01:51:43 +00:00
EX void vr_sphere ( hyperpoint & ret , hyperpoint & H , eModel md ) {
ret = H ;
int flip = 1 ;
if ( md = = mdHalfplane ) flip = - flip ;
if ( pconf . alpha < 1 ) flip = - flip ;
ret * = pow ( sqhypot_d ( 3 , H ) , ( flip * pconf . depth_scaling - 1 ) / 2 ) ;
ret [ 2 ] + = pconf . alpha ;
if ( md = = mdHalfplane ) {
ld d = sqhypot_d ( 3 , ret ) ;
ret / = abs ( d ) ;
}
}
void vr_disk ( hyperpoint & ret , hyperpoint & H ) {
if ( euclid ) {
ret = H ;
ret [ 2 ] = vid . depth * ( 1 - ( ret [ 2 ] - 1 ) * pconf . depth_scaling ) + pconf . alpha + vid . camera ;
}
else if ( sphere ) {
vr_sphere ( ret , H , mdDisk ) ;
return ;
}
else {
ld zlev = find_zlev ( H ) ;
ld zl = vid . depth - geom3 : : factor_to_lev ( zlev ) * pconf . depth_scaling ;
ld d = hdist0 ( H ) ;
ld dd = hypot_d ( 2 , H ) ;
hyperpoint H1 = ypush ( vid . camera ) * xpush ( d ) * ypush0 ( zl ) ;
ld tzh = pconf . alpha + H1 [ 2 ] ;
ld ax = H1 [ 0 ] / tzh ;
ld ay = H1 [ 1 ] / tzh ;
ret [ 0 ] = ax * H [ 0 ] / dd ;
ret [ 1 ] = ax * H [ 1 ] / dd ;
ret [ 2 ] = ay ;
}
}
2021-03-30 09:27:48 +00:00
# if MAXMDIM >= 4
2021-03-06 10:54:25 +00:00
/** Compute the three-point projection. Currently only works in isotropic 3D spaces. */
EX void threepoint_projection ( const hyperpoint & H , hyperpoint & ret ) {
hyperpoint H1 = H ;
2021-03-06 10:59:57 +00:00
find_zlev ( H1 ) ;
2021-03-06 10:54:25 +00:00
if ( true ) {
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H1 ) ;
2021-03-06 10:54:25 +00:00
}
auto p = pconf . twopoint_param ;
ld dist [ 3 ] ;
for ( int i = 0 ; i < 3 ; i + + ) {
2022-11-12 21:38:45 +00:00
hyperpoint h1 = xspinpush0 ( TAU * i / 3 , p ) ;
2021-03-06 10:54:25 +00:00
dist [ i ] = geo_dist ( h1 , H1 ) ;
}
/* we are looking for the points (x,y,z) such that:
( x - xi ) ^ 2 + ( y - yi ) ^ 2 + z ^ 2 = di ^ 2
which is equivalent to :
x ^ 2 + y ^ 2 + z ^ 2 - 2 xxi - 2 yyi = di ^ 2 - xi ^ 2 - yi ^ 2
After setting s = x ^ 2 + y ^ 2 + z ^ 2 , we get a system of linear equations for ( x , y , s )
*/
dynamicval < eGeometry > g ( geometry , gEuclid ) ;
transmatrix T = Id ;
hyperpoint v = C0 ;
for ( int i = 0 ; i < 3 ; i + + ) {
2022-11-12 21:38:45 +00:00
hyperpoint pp = xspinpush0 ( TAU * i / 3 , p ) ;
2021-03-06 10:54:25 +00:00
v [ i ] = dist [ i ] * dist [ i ] - p * p ;
T [ i ] [ 0 ] = - 2 * pp [ 0 ] ;
T [ i ] [ 1 ] = - 2 * pp [ 1 ] ;
T [ i ] [ 2 ] = 1 ;
}
transmatrix U = inverse3 ( T ) ;
hyperpoint sxy = U * v ;
// compute the actual z based on s
sxy [ 2 ] = sxy [ 2 ] - sqhypot_d ( 2 , sxy ) ;
sxy [ 2 ] = sxy [ 2 ] > 0 ? sqrt ( sxy [ 2 ] ) : 0 ;
if ( H1 [ 2 ] < 0 ) sxy [ 2 ] * = - 1 ;
sxy [ 3 ] = 1 ;
geometry = gCubeTiling ;
ret = sxy ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2021-03-06 10:54:25 +00:00
}
2021-03-30 09:27:48 +00:00
# endif
2021-03-06 10:54:25 +00:00
2022-03-27 07:05:47 +00:00
EX vector < hr : : function < void ( shiftpoint & H_orig , hyperpoint & H , hyperpoint & ret ) > > extra_projections ;
2023-03-16 22:13:27 +00:00
EX void make_axial ( hyperpoint H , hyperpoint & ret , const hr : : function < ld ( hyperpoint ) > & f ) {
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2023-03-16 22:13:27 +00:00
ret [ 0 ] = f ( H ) ;
ld axi = pconf . axial_angle ;
bool ax = GDIM = = 3 | | ( axi / 180 - floor ( axi / 180 ) ) = = 0.5 ;
if ( ax ) {
ret [ 1 ] = f ( cspin90 ( 0 , 1 ) * H ) ;
ret [ 2 ] = 0 ;
if ( GDIM = = 3 ) ret [ 2 ] = f ( cspin90 ( 2 , 1 ) * H ) ;
}
else {
ld alpha = axi * degree ;
ld val = f ( cspin ( 0 , 1 , alpha ) * H ) ;
// ret[0] * cos(alpha) + ret[1] * sin(alpha) == val
ret [ 1 ] = ( val - ret [ 0 ] * cos ( alpha ) ) / sin ( alpha ) ;
ret [ 2 ] = 0 ;
}
ret [ 3 ] = 1 ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2023-03-16 22:13:27 +00:00
}
2023-03-19 11:21:05 +00:00
// according to https://github.com/cspersonal/peirce-quincuncial-projection/blob/master/peirceQuincuncialProjection.R
ld ellRF ( ld x , ld y , ld z ) {
ld delx = 1 , dely = 1 , delz = 1 ;
const ld eps = 0.0025 ;
ld mean ;
while ( abs ( delx ) > eps | | abs ( dely ) > eps | | abs ( delz ) > eps ) {
ld sx = sqrt ( x ) ;
ld sy = sqrt ( y ) ;
ld sz = sqrt ( z ) ;
ld len = sx * ( sy + sz ) + sy * sz ;
x = .25 * ( x + len ) ;
y = .25 * ( y + len ) ;
z = .25 * ( z + len ) ;
mean = ( x + y + z ) / 3 ;
delx = ( mean - x ) / mean ;
dely = ( mean - y ) / mean ;
delz = ( mean - z ) / mean ;
}
ld e2 = delx * dely - delz * delz ;
ld e3 = delx * dely * delz ;
return ( ( 1.0 + ( e2 / 24.0 - 0.1 - 3.0 * e3 / 44.0 ) * e2 + e3 / 14 ) / sqrt ( mean ) ) ;
}
ld ellFaux ( ld cos_phi , ld sin_phi , ld k ) {
ld x = cos_phi * cos_phi ;
ld y = 1 - k * k * sin_phi * sin_phi ;
return sin_phi * ellRF ( x , y , 1 ) ;
}
ld sqrt_clamp ( ld x ) { if ( x < 0 ) return 0 ; return sqrt ( x ) ; }
2024-05-25 09:46:53 +00:00
EX hyperpoint to_square ( hyperpoint H ) {
2023-03-19 11:21:05 +00:00
ld d = hypot_d ( 2 , H ) ;
ld x = d / ( H [ 2 ] + 1 ) ;
x * = pconf . model_transition ;
ld cos_phiosqrt2 = sqrt ( 2 ) / ( x + 1 / x ) ;
ld cos_lambda = - H [ 1 ] / d ;
ld sin_lambda = H [ 0 ] / d ;
ld cos_a = cos_phiosqrt2 * ( sin_lambda + cos_lambda ) ;
ld cos_b = cos_phiosqrt2 * ( sin_lambda - cos_lambda ) ;
ld sin_a = sqrt ( 1 - cos_a * cos_a ) ;
ld sin_b = sqrt ( 1 - cos_b * cos_b ) ;
ld cos_a_cos_b = cos_a * cos_b ;
ld sin_a_sin_b = sin_a * sin_b ;
ld sin2_m = 1.0 + cos_a_cos_b - sin_a_sin_b ;
ld sin2_n = 1.0 - cos_a_cos_b - sin_a_sin_b ;
ld sin_m = sqrt_clamp ( sin2_m ) ;
ld cos_m = sqrt_clamp ( 1 - sin2_m ) ;
if ( sin_lambda < 0 ) sin_m = - sin_m ;
ld sin_n = sqrt_clamp ( sin2_n ) ;
ld cos_n = sqrt_clamp ( 1.0 - sin2_n ) ;
if ( cos_lambda > 0.0 ) sin_n = - sin_n ;
hyperpoint res ;
ld divby = 0.53935260118837935472 ;
res [ 0 ] = ellFaux ( cos_m , sin_m , sqrt ( 2 ) / 2. ) * divby ;
res [ 1 ] = ellFaux ( cos_n , sin_n , sqrt ( 2 ) / 2. ) * divby ;
res [ 2 ] = 0 ; res [ 3 ] = 1 ;
if ( x > 1 ) {
if ( abs ( res [ 0 ] ) > abs ( res [ 1 ] ) ) {
if ( res [ 0 ] > 0 ) res [ 0 ] = 2 - res [ 0 ] ; else res [ 0 ] = - 2 - res [ 0 ] ;
}
else {
if ( res [ 1 ] > 0 ) res [ 1 ] = 2 - res [ 1 ] ; else res [ 1 ] = - 2 - res [ 1 ] ;
}
}
res / = pconf . model_transition ;
return res ;
}
2023-08-15 12:21:19 +00:00
EX hyperpoint hyperboloid_form ( hyperpoint ret ) {
ret = cspin90 ( 2 , 1 ) * ret / 3 ;
if ( hyperbolic ) ret [ 1 ] + = 1 / 3. ;
ret = pconf . ball ( ) * ret ;
return ret ;
}
2024-10-05 11:45:16 +00:00
EX void product_projection ( hyperpoint H , hyperpoint & ret , eModel proj ) {
ld zlev = zlevel ( H ) ;
H / = exp ( zlev ) ;
H = space_to_perspective ( H ) ;
H [ 1 ] + = 1 ;
double rad = sqhypot_d ( 2 , H ) ;
H / = rad ;
H [ 1 ] - = 0.5 ;
H [ 1 ] = - H [ 1 ] ;
H [ 2 ] = 0 ; H [ 3 ] = 1 ; ret = H ;
tie ( H [ 1 ] , H [ 2 ] ) = make_pair ( H [ 1 ] * cos ( zlev ) , H [ 1 ] * sin ( zlev ) ) ;
if ( proj = = mdDisk ) {
H [ 1 ] = - H [ 1 ] ;
H [ 1 ] + = 0.5 ;
rad = sqhypot_d ( 3 , H ) ;
H [ 0 ] / = rad ; H [ 1 ] / = rad ; H [ 2 ] / = rad ;
H [ 1 ] - = 1 ;
}
H [ 3 ] = 1 ;
ret = NLP * H ;
}
2020-09-15 17:17:07 +00:00
EX void apply_other_model ( shiftpoint H_orig , hyperpoint & ret , eModel md ) {
2019-02-21 17:46:53 +00:00
2020-07-27 16:49:04 +00:00
hyperpoint H = H_orig . h ;
2019-02-06 21:41:45 +00:00
2020-09-15 17:17:07 +00:00
if ( models : : product_model ( md ) ) {
2020-07-27 16:49:04 +00:00
ld zlev = zlevel ( H_orig . h ) ;
H_orig . h / = exp ( zlev ) ;
hybrid : : in_underlying_geometry ( [ & ] { applymodel ( H_orig , ret ) ; } ) ;
2020-04-16 22:53:58 +00:00
ret [ 2 ] = zlev * pconf . product_z_scale ;
2019-11-14 16:20:55 +00:00
ret = NLP * ret ;
2019-11-09 12:14:42 +00:00
return ;
}
2020-09-15 17:17:07 +00:00
switch ( md ) {
2019-03-20 01:10:53 +00:00
case mdPerspective : {
2022-12-08 18:38:06 +00:00
if ( gproduct ) H = product : : inverse_exp ( H ) ;
2020-09-11 09:13:18 +00:00
apply_nil_rotation ( H ) ;
2023-05-15 00:25:13 +00:00
if ( ! computing_semidirect ) H = lp_apply ( H ) ;
2020-09-11 09:13:18 +00:00
apply_perspective ( H , ret ) ;
return ;
2019-03-20 01:10:53 +00:00
}
2019-07-28 09:07:21 +00:00
2019-08-06 14:48:01 +00:00
case mdGeodesic : {
2023-05-15 00:25:13 +00:00
auto S = inverse_exp ( H_orig , pNORMAL | pfNO_DISTANCE ) ;
if ( ! computing_semidirect ) S = lp_apply ( S ) ;
2020-09-11 09:13:18 +00:00
apply_perspective ( S , ret ) ;
2019-07-28 09:07:21 +00:00
return ;
}
2019-03-20 01:10:53 +00:00
2022-05-17 07:40:33 +00:00
case mdLiePerspective : {
if ( false ) {
hyperpoint h = point31 ( 0 , 0 , 1 ) ;
hyperpoint a = point31 ( 0 , 0 , 0 ) ;
hyperpoint b = point31 ( 0.1 , 0 , 0 ) ;
println ( hlog , rgpushxto0 ( h ) * a ) ;
println ( hlog , rgpushxto0 ( h ) * b ) ;
exit ( 1 ) ;
/* x wanes as z grows! */
}
2022-12-25 11:14:36 +00:00
hyperpoint S = lie_log_correct ( H_orig , H ) ;
2023-04-14 23:18:47 +00:00
# if MAXMDIM >= 4
2022-12-25 11:14:36 +00:00
S [ 3 ] = 1 ;
2023-04-14 23:18:47 +00:00
# endif
2023-05-15 00:25:13 +00:00
if ( ! computing_semidirect ) S = lp_apply ( S ) ;
2022-05-17 07:40:33 +00:00
if ( hyperbolic ) {
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2022-05-17 07:40:33 +00:00
}
apply_perspective ( S , ret ) ;
return ;
}
2023-04-14 23:18:47 +00:00
# if MAXMDIM >= 4
2022-10-13 22:56:48 +00:00
case mdRelPerspective : {
2022-12-25 11:14:36 +00:00
auto S = rel_log ( H_orig , true ) ; S [ 3 ] = 1 ;
2023-05-15 00:25:13 +00:00
if ( ! computing_semidirect ) S = lp_apply ( S ) ;
2022-10-13 22:56:48 +00:00
apply_perspective ( S , ret ) ;
return ;
}
2023-04-14 23:18:47 +00:00
# endif
2022-10-13 22:56:48 +00:00
2019-10-21 20:34:20 +00:00
case mdPixel :
2018-11-17 18:24:02 +00:00
ret = H / current_display - > radius ;
2018-11-06 14:53:50 +00:00
return ;
2018-10-26 19:03:27 +00:00
2018-11-06 14:53:50 +00:00
case mdBall : {
2020-12-30 13:20:30 +00:00
if ( vrhr : : rendering ( ) ) {
2020-12-29 01:51:43 +00:00
vr_disk ( ret , H ) ;
return ;
}
2018-11-06 14:53:50 +00:00
ld zlev = find_zlev ( H ) ;
2020-12-28 21:00:56 +00:00
ld zl = vid . depth - geom3 : : factor_to_lev ( zlev ) * pconf . depth_scaling ;
2018-10-26 19:03:27 +00:00
2018-11-06 14:53:50 +00:00
ballmodel ( ret , atan2 ( H ) , hdist0 ( H ) , zl ) ;
break ;
}
case mdDisk : {
2024-10-05 11:45:16 +00:00
if ( mproduct & & pconf . alpha = = 1 ) {
product_projection ( H , ret , mdDisk ) ;
break ;
}
2019-10-05 10:34:14 +00:00
if ( nonisotropic ) {
2020-07-27 16:49:04 +00:00
ret = lp_apply ( inverse_exp ( H_orig , pNORMAL | pfNO_DISTANCE ) ) ;
2019-10-05 10:34:14 +00:00
ld w ;
2019-12-14 11:28:45 +00:00
if ( sn : : in ( ) ) {
2019-10-05 10:34:14 +00:00
// w = 1 / sqrt(1 - sqhypot_d(3, ret));
2020-04-16 22:53:58 +00:00
// w = w / (pconf.alpha + w);
w = 1 / ( sqrt ( 1 - sqhypot_d ( 3 , ret ) ) * pconf . alpha + 1 ) ;
2019-10-05 10:34:14 +00:00
}
else {
w = hypot_d ( 3 , ret ) ;
2023-07-21 07:29:49 +00:00
if ( w ) w = sinh ( w ) / ( ( pconf . alpha + cosh ( w ) ) * w ) ;
2019-10-05 10:34:14 +00:00
}
for ( int i = 0 ; i < 3 ; i + + ) ret [ i ] * = w ;
ret [ 3 ] = 1 ;
break ;
}
2020-12-30 17:49:56 +00:00
if ( vrhr : : rendering ( ) & & WDIM = = 2 ) {
2020-12-29 01:51:43 +00:00
vr_disk ( ret , H ) ;
2020-12-28 21:00:56 +00:00
return ;
}
2018-11-06 14:53:50 +00:00
ld tz = get_tz ( H ) ;
2023-08-14 16:08:28 +00:00
if ( models : : camera_straight ) {
2018-11-06 14:53:50 +00:00
ret [ 0 ] = H [ 0 ] / tz ;
ret [ 1 ] = H [ 1 ] / tz ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) ret [ 2 ] = H [ 2 ] / tz ;
2019-03-20 01:10:53 +00:00
else ret [ 2 ] = vid . xres * current_display - > eyewidth ( ) / 2 / current_display - > radius - vid . ipd / tz / 2 ;
if ( MAXMDIM = = 4 ) ret [ 3 ] = 1 ;
2018-11-06 14:53:50 +00:00
}
else {
ld tx = H [ 0 ] ;
ld ty = H [ 1 ] ;
2023-08-14 16:08:28 +00:00
hyperpoint p = hyperpoint ( tx , ty , tz , 1 ) ;
p = rot_inverse ( pconf . cam ( ) ) * p ;
ret [ 0 ] = p [ 0 ] / p [ 2 ] ;
ret [ 1 ] = p [ 1 ] / p [ 2 ] ;
ret [ 2 ] = vid . xres * current_display - > eyewidth ( ) / 2 / current_display - > radius - vid . ipd / p [ 2 ] / 2 ;
2018-03-27 02:01:30 +00:00
}
2018-11-06 14:53:50 +00:00
return ;
2019-07-03 02:53:56 +00:00
}
case mdCentralInversion : {
ld tz = get_tz ( H ) ;
2019-08-15 13:05:43 +00:00
for ( int d = 0 ; d < GDIM ; d + + ) ret [ d ] = H [ d ] / tz ;
2020-04-17 00:22:01 +00:00
for ( int d = GDIM ; d < MAXMDIM ; d + + ) ret [ d ] = 1 ;
2019-07-03 02:53:56 +00:00
ld r = 0 ;
2019-08-15 13:05:43 +00:00
for ( int d = 0 ; d < GDIM ; d + + ) r + = ret [ d ] * ret [ d ] ;
for ( int d = 0 ; d < GDIM ; d + + ) ret [ d ] / = r ;
2019-07-03 02:53:56 +00:00
return ;
2018-03-26 17:06:47 +00:00
}
2018-11-06 14:53:50 +00:00
case mdHalfplane : {
2024-10-05 11:45:16 +00:00
if ( mproduct ) {
product_projection ( H , ret , mdHalfplane ) ;
break ;
}
2020-12-30 13:20:30 +00:00
if ( sphere & & vrhr : : rendering ( ) ) {
2020-12-29 01:51:43 +00:00
vr_sphere ( ret , H , md ) ;
return ;
}
2018-11-06 14:53:50 +00:00
// Poincare to half-plane
ld zlev = find_zlev ( H ) ;
H = space_to_perspective ( H ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2018-11-06 14:53:50 +00:00
H [ 1 ] + = 1 ;
2019-08-15 13:05:43 +00:00
double rad = sqhypot_d ( GDIM , H ) ;
2018-11-06 14:53:50 +00:00
H / = - rad ;
H [ 1 ] + = .5 ;
2018-10-23 14:58:19 +00:00
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2019-03-20 17:32:03 +00:00
// a bit simpler when we do not care about 3D
2020-04-16 22:53:58 +00:00
H * = pconf . halfplane_scale ;
2019-03-20 17:32:03 +00:00
ret [ 0 ] = - H [ 0 ] ;
ret [ 1 ] = 1 + H [ 1 ] ;
ret [ 2 ] = H [ 2 ] ;
ret [ 3 ] = 1 ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2019-03-20 17:32:03 +00:00
break ;
}
2023-08-14 09:24:29 +00:00
/* it was inverted, so we apply scr_to_ori again */
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2020-04-16 22:53:58 +00:00
H * = pconf . halfplane_scale ;
2023-08-08 14:27:52 +00:00
auto ocos = pconf . mori ( ) . get ( ) [ 0 ] [ 0 ] ;
auto osin = pconf . mori ( ) . get ( ) [ 1 ] [ 0 ] ;
ret [ 0 ] = - osin - H [ 0 ] ;
2020-12-27 16:14:50 +00:00
ld height = 0 ;
2018-11-06 14:53:50 +00:00
if ( zlev ! = 1 ) {
2023-08-08 14:27:52 +00:00
if ( abs ( ocos ) > 1e-9 )
height + = H [ 1 ] * ( pow ( zlev , ocos ) - 1 ) ;
if ( abs ( ocos ) > 1e-9 & & osin )
height + = H [ 0 ] * osin * ( pow ( zlev , ocos ) - 1 ) / ocos ;
else if ( osin )
height + = H [ 0 ] * osin * log ( zlev ) ;
2018-11-06 14:53:50 +00:00
}
2023-08-08 14:27:52 +00:00
ret [ 1 ] = ocos + H [ 1 ] ;
2019-08-15 13:05:43 +00:00
ret [ 2 ] = GDIM = = 3 ? H [ 2 ] : 0 ;
2019-03-20 01:10:53 +00:00
if ( MAXMDIM = = 4 ) ret [ 3 ] = 1 ;
2023-08-14 09:24:29 +00:00
if ( zlev ! = 1 & & use_z_coordinate ( ) )
2020-12-27 16:14:50 +00:00
apply_depth ( ret , height ) ;
else
ret [ 1 ] + = height * pconf . depth_scaling ;
2018-11-06 14:53:50 +00:00
break ;
}
2023-03-16 22:13:27 +00:00
2020-08-20 14:02:34 +00:00
case mdAxial : {
2023-03-16 22:13:27 +00:00
make_axial ( H , ret , [ ] ( hyperpoint H ) {
ld & mt = pconf . model_transition ;
ld z = H [ LDIM ] ;
if ( mt ! = 1 ) z + = ( 1 - mt ) * pconf . alpha ;
ld res = H [ 0 ] / z ;
if ( mt ) {
if ( mt < 1 ) res * = mt ;
res = atan_auto ( res * mt ) ;
if ( mt > 1 ) res / = mt ;
}
return res ;
} ) ;
2020-08-20 14:02:34 +00:00
if ( sphere ) ret [ 0 ] + = axial_x * M_PI , ret [ 1 ] + = axial_y * M_PI ;
break ;
}
case mdAntiAxial : {
2023-03-16 22:13:27 +00:00
make_axial ( H , ret , [ ] ( hyperpoint H ) { return asin_auto ( H [ 0 ] ) ; } ) ;
2020-08-20 14:02:34 +00:00
break ;
}
case mdQuadrant : {
H = space_to_perspective ( H ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2020-08-20 14:02:34 +00:00
tie ( H [ 0 ] , H [ 1 ] ) = make_pair ( ( H [ 0 ] + H [ 1 ] ) / sqrt ( 2 ) , ( H [ 1 ] - H [ 0 ] ) / sqrt ( 2 ) ) ;
H [ 1 ] + = 1 ;
double rad = sqhypot_d ( GDIM , H ) ;
H / = - rad ;
H [ 1 ] + = .5 ;
H * = 2 ;
ld x = exp ( - H [ 0 ] / 2 ) ;
ret [ 0 ] = - H [ 1 ] * x - 1 ;
ret [ 1 ] = H [ 1 ] / x + 1 ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2020-08-20 14:02:34 +00:00
break ;
}
2019-11-09 11:49:00 +00:00
case mdHorocyclic : {
2022-10-13 22:54:31 +00:00
if ( sl2 ) {
2024-07-15 11:56:08 +00:00
optimize_shift ( H_orig ) ;
ret [ 2 ] = H_orig . shift ;
ld d = hypot_d ( 2 , H_orig . h ) ;
ld z = acosh ( H_orig . h [ 3 ] ) ;
ret [ 0 ] = H_orig . h [ 0 ] * z / d ;
ret [ 1 ] = H_orig . h [ 1 ] * z / d ;
ret [ 3 ] = 1 ;
2022-10-13 22:54:31 +00:00
}
2019-11-09 11:49:00 +00:00
find_zlev ( H ) ;
2020-09-11 09:13:18 +00:00
apply_nil_rotation ( H ) ;
2019-11-09 11:49:00 +00:00
2023-08-14 09:08:37 +00:00
if ( hyperbolic ) models : : scr_to_ori ( H ) ;
2019-11-09 11:49:00 +00:00
2021-10-07 11:23:44 +00:00
ret = hyperbolic ? deparabolic13 ( H ) : H ;
2019-11-09 11:49:00 +00:00
ret * = .5 ;
ret [ LDIM ] = 1 ;
2023-08-14 09:08:37 +00:00
if ( hyperbolic ) models : : ori_to_scr ( ret ) ;
2019-11-09 11:49:00 +00:00
2022-12-25 11:14:36 +00:00
if ( ! vrhr : : rendering ( ) ) ret = lp_apply ( ret ) ;
2020-09-11 09:13:18 +00:00
2019-11-09 11:49:00 +00:00
break ;
}
2022-05-17 07:40:33 +00:00
2023-03-16 13:42:02 +00:00
case mdHorocyclicEqa : {
2023-08-14 09:08:37 +00:00
if ( hyperbolic ) models : : scr_to_ori ( H ) ;
2023-03-16 13:42:02 +00:00
ret = hyperbolic ? deparabolic13 ( H ) : H ;
ret [ 0 ] = exp ( - ret [ 0 ] ) - 1 ;
ret * = .5 ;
ret [ LDIM ] = 1 ;
2023-08-14 09:08:37 +00:00
if ( hyperbolic ) models : : ori_to_scr ( ret ) ;
2023-03-16 13:42:02 +00:00
break ;
}
2023-03-19 11:21:05 +00:00
case mdConformalSquare : {
find_zlev ( H ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2023-04-11 14:44:11 +00:00
if ( euclid ) H [ 0 ] / = pconf . fisheye_param , H [ 1 ] / = pconf . fisheye_param ;
2023-03-19 11:21:05 +00:00
ret = to_square ( H ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2023-03-19 11:21:05 +00:00
break ;
}
2022-05-17 07:40:33 +00:00
case mdLieOrthogonal : {
2022-12-25 11:14:36 +00:00
ret = lie_log_correct ( H_orig , H ) ;
2022-05-17 07:40:33 +00:00
ret * = .5 ;
ret [ LDIM ] = 1 ;
2023-08-14 09:08:37 +00:00
if ( hyperbolic ) models : : ori_to_scr ( ret ) ;
2022-05-17 07:40:33 +00:00
2022-12-25 11:14:36 +00:00
if ( ! vrhr : : rendering ( ) ) ret = lp_apply ( ret ) ;
2022-05-17 07:40:33 +00:00
break ;
}
2023-04-14 23:18:47 +00:00
# if MAXMDIM >= 4
2022-10-13 22:56:48 +00:00
case mdRelOrthogonal : {
2022-12-25 11:14:36 +00:00
ret = rel_log ( H_orig , true ) ;
2022-10-13 22:56:48 +00:00
ret * = .5 ;
ret [ LDIM ] = 1 ;
2022-12-25 11:14:36 +00:00
if ( ! vrhr : : rendering ( ) ) ret = lp_apply ( ret ) ;
2022-10-13 22:56:48 +00:00
break ;
}
2023-04-14 23:18:47 +00:00
# endif
2022-10-13 22:56:48 +00:00
2018-11-06 14:53:50 +00:00
case mdHemisphere : {
2020-12-27 16:50:26 +00:00
# if CAP_VR
2020-12-30 13:20:30 +00:00
ld dir = vrhr : : rendering ( ) ? - 1 : 1 ;
2020-12-27 16:50:26 +00:00
# else
constexpr ld dir = 1 ;
# endif
2018-11-06 14:53:50 +00:00
switch ( cgclass ) {
case gcHyperbolic : {
2024-06-28 12:00:52 +00:00
if ( pconf . small_hyperboloid ) H = mid ( C0 , H ) ;
2018-11-06 14:53:50 +00:00
ld zl = zlevel ( H ) ;
ret = H / H [ 2 ] ;
2019-02-27 18:33:13 +00:00
ret [ 2 ] = sqrt ( 1 - sqhypot_d ( 2 , ret ) ) ;
2020-12-27 16:37:39 +00:00
// need to reverse in VR
ret = ret * ( 1 + ( zl - 1 ) * ret [ 2 ] * pconf . depth_scaling * dir ) ;
2018-07-30 15:44:11 +00:00
break ;
}
2018-11-06 14:53:50 +00:00
2019-07-23 13:08:07 +00:00
case gcEuclid : default : {
2018-11-06 14:53:50 +00:00
// stereographic projection to a sphere
2020-04-16 22:53:58 +00:00
auto hd = hdist0 ( H ) / pconf . euclid_to_sphere ;
2019-02-22 19:58:40 +00:00
if ( hd = = 0 ) ret = hpxyz ( 0 , 0 , - 1 ) ;
2018-11-06 14:53:50 +00:00
else {
ld x = 2 * hd / ( 1 + hd * hd ) ;
ld y = x / hd ;
2020-04-16 22:53:58 +00:00
ret = H * x / hd / pconf . euclid_to_sphere ;
2018-11-06 14:53:50 +00:00
ret [ 2 ] = ( 1 - y ) ;
2020-12-27 16:50:26 +00:00
ret [ 2 ] * = dir ;
ret = ret * ( 1 + ( H [ 2 ] - 1 ) * y * pconf . depth_scaling * dir / pconf . euclid_to_sphere ) ;
2024-06-28 12:00:52 +00:00
if ( pconf . small_hyperboloid ) { ret = ret - C0 ; ret = ret / hypot_d ( 3 , ret ) ; }
2018-04-21 10:18:33 +00:00
}
2018-07-30 15:44:11 +00:00
break ;
2018-04-21 10:18:33 +00:00
}
2018-11-06 14:53:50 +00:00
case gcSphere : {
2024-06-28 12:00:52 +00:00
if ( pconf . small_hyperboloid ) H = mid ( C0 , H ) ;
2020-12-30 13:20:30 +00:00
if ( vrhr : : rendering ( ) ) { vr_sphere ( ret , H , md ) ; return ; }
2022-04-07 18:50:16 +00:00
ld z = sqhypot_d ( 3 , H ) ;
int s = H [ 2 ] > 0 ? 1 : - 1 ;
2018-11-06 14:53:50 +00:00
ret = H ;
2022-04-07 18:50:16 +00:00
ret / = ret [ 2 ] ;
ret [ 2 ] = sqrt ( 1 + ret [ 0 ] * ret [ 0 ] + ret [ 1 ] * ret [ 1 ] ) * s ;
ret * = z ;
ld & topz = pconf . top_z ;
if ( abs ( ret [ 2 ] ) > topz | | ( hemi_side & & s ! = hemi_side ) ) {
ld scale = sqrt ( topz * topz - 1 ) / hypot_d ( 2 , ret ) ;
ret * = scale ;
ret [ 2 ] = topz * s ;
}
2020-12-27 16:37:39 +00:00
if ( pconf . depth_scaling ! = 1 ) {
ld v = intval ( H , Hypc ) ;
2020-12-27 16:50:26 +00:00
ret * = pow ( v , ( dir * pconf . depth_scaling - 1 ) / 2 ) ;
2020-12-27 16:37:39 +00:00
}
2022-04-07 18:50:16 +00:00
ret / = 3 ;
2018-03-26 17:06:47 +00:00
break ;
2018-07-30 15:44:11 +00:00
}
2018-03-26 17:06:47 +00:00
}
2020-12-27 16:37:39 +00:00
2020-12-31 01:53:25 +00:00
if ( vrhr : : rendering ( ) ) return ;
2018-11-06 14:53:50 +00:00
swap ( ret [ 1 ] , ret [ 2 ] ) ;
2023-08-14 14:18:44 +00:00
ret = pconf . ball ( ) * ret ;
2018-11-06 14:53:50 +00:00
break ;
2018-03-26 17:06:47 +00:00
}
2017-11-13 00:29:31 +00:00
2018-11-06 14:53:50 +00:00
case mdHyperboloidFlat :
case mdHyperboloid : {
2019-10-05 10:34:14 +00:00
if ( nonisotropic ) {
2019-11-14 16:20:55 +00:00
// if(nisot::local_perspective_used()) H = NLP * H;
2019-11-09 11:32:37 +00:00
ret = lp_apply ( H ) ;
2019-10-05 10:34:14 +00:00
break ;
}
2022-12-08 18:38:06 +00:00
if ( gproduct ) {
2019-11-09 12:14:42 +00:00
ret = H ;
break ;
}
2020-12-27 16:14:50 +00:00
2020-12-27 16:37:39 +00:00
# if CAP_VR
2020-12-30 13:20:30 +00:00
if ( vrhr : : rendering ( ) ) {
2020-12-29 01:51:43 +00:00
if ( sphere ) { vr_sphere ( ret , H , md ) ; return ; }
2020-12-27 16:14:50 +00:00
ret [ 0 ] = H [ 0 ] * pconf . hyperboloid_scaling ;
ret [ 1 ] = H [ 1 ] * pconf . hyperboloid_scaling ;
ret [ 2 ] = ( pconf . alpha + H [ 2 ] ) ;
if ( pconf . depth_scaling ! = 1 ) {
ld v = intval ( H , Hypc ) ;
2020-12-29 01:51:43 +00:00
ret * = pow ( v , ( pconf . depth_scaling - 1 ) / 2 ) ;
2020-12-27 16:14:50 +00:00
}
2020-12-31 01:53:25 +00:00
return ;
2020-12-27 16:14:50 +00:00
}
2020-12-27 16:37:39 +00:00
# endif
2020-12-27 16:14:50 +00:00
2024-06-27 20:32:09 +00:00
if ( pconf . small_hyperboloid ) H = mid ( H , C0 ) ;
2022-04-07 18:50:16 +00:00
ret = H ;
if ( sphere & & pmodel = = mdHyperboloidFlat ) {
int s = H [ 2 ] > 0 ? 1 : - 1 ;
ret / = ret [ 2 ] ;
ret [ 2 ] = sqrt ( 1 + ret [ 0 ] * ret [ 0 ] + ret [ 1 ] * ret [ 1 ] ) * s ;
}
2020-12-27 16:37:39 +00:00
if ( pconf . depth_scaling ! = 1 ) {
2022-04-07 18:50:16 +00:00
ld v = intval ( ret , Hypc ) ;
ret * = pow ( v , ( pconf . depth_scaling - 1 ) / 2 ) ;
2020-12-27 16:37:39 +00:00
}
2018-11-06 14:53:50 +00:00
if ( pmodel = = mdHyperboloid ) {
2020-04-16 22:53:58 +00:00
ld & topz = pconf . top_z ;
2022-04-07 18:50:16 +00:00
if ( ret [ 2 ] > topz ) {
ld scale = sqrt ( topz * topz - 1 ) / hypot_d ( 2 , ret ) ;
ret * = scale ;
ret [ 2 ] = topz ;
2018-11-06 14:53:50 +00:00
}
}
else {
2022-04-07 18:50:16 +00:00
ret = space_to_perspective ( ret , pconf . alpha ) ;
ret [ 2 ] = 1 - pconf . alpha ;
if ( sphere ) ret [ 2 ] = - ret [ 2 ] ;
2018-11-06 14:53:50 +00:00
}
2017-07-10 18:47:38 +00:00
2023-08-15 12:21:19 +00:00
ret = hyperboloid_form ( ret ) ;
2018-11-06 14:53:50 +00:00
break ;
}
2018-10-26 19:03:27 +00:00
2018-11-06 14:53:50 +00:00
case mdFisheye : {
2019-10-05 10:34:14 +00:00
ld zlev ;
if ( nonisotropic ) {
2020-07-27 16:49:04 +00:00
H = lp_apply ( inverse_exp ( H_orig ) ) ;
2019-10-05 10:34:14 +00:00
zlev = 1 ;
}
else {
zlev = find_zlev ( H ) ;
H = space_to_perspective ( H ) ;
}
2020-04-16 22:53:58 +00:00
H / = pconf . fisheye_param ;
2019-08-17 21:28:41 +00:00
H [ LDIM ] = zlev ;
2019-08-15 13:05:43 +00:00
ret = H / sqrt ( 1 + sqhypot_d ( GDIM + 1 , H ) ) ;
2019-08-17 21:28:41 +00:00
if ( GDIM = = 3 ) ret [ LDIM ] = zlev ;
2018-11-06 14:53:50 +00:00
break ;
}
2018-10-26 19:03:27 +00:00
2024-01-07 11:52:50 +00:00
case mdFisheye2 : {
ld zlev ;
if ( nonisotropic ) {
H = lp_apply ( inverse_exp ( H_orig ) ) ;
zlev = 1 ;
}
else {
zlev = find_zlev ( H ) ;
H = space_to_perspective ( H ) ;
}
H / = pconf . fisheye_param ;
auto H1 = perspective_to_space ( H , pconf . fisheye_alpha , gcSphere ) ;
auto H2 = perspective_to_space ( hyperpoint ( 1e6 , 0 , 0 , 0 ) , pconf . fisheye_alpha , gcSphere ) ;
H1 [ 2 ] + = 1 ;
H1 / = H1 [ 2 ] ;
H1 / = H2 [ 0 ] / ( H2 [ 2 ] + 1 ) ;
ret = H1 ;
if ( GDIM = = 3 ) ret [ LDIM ] = zlev ;
break ;
}
2019-07-12 21:10:01 +00:00
case mdSimulatedPerspective : {
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2019-07-12 21:10:01 +00:00
auto yz = move_z_to_y ( H ) ;
2020-04-16 22:53:58 +00:00
hyperpoint Hl = xpush ( - pconf . twopoint_param ) * H ;
hyperpoint Hr = xpush ( + pconf . twopoint_param ) * H ;
2019-07-12 21:10:01 +00:00
ld lyx = ( Hl [ 1 ] + 1e-7 ) / ( Hl [ 0 ] + 1e-8 ) ;
ld ryx = ( Hr [ 1 ] + 1e-7 ) / ( Hr [ 0 ] + 1e-8 ) ;
// (r.x + t) * lyx = (r.x - t) * ryx = r.y
// r.x * lyx + t * lyx = r.x * ryx - t * ryx
// r.x * (lyx-ryx) = - t * (ryx + lyx)
// r.x = -t * (ryx+lyx) / (lyx-ryx)
// r.x = - 2 * t * lyx * ryx / lyx / ryx
2020-04-16 22:53:58 +00:00
ret [ 0 ] = - pconf . twopoint_param * ( ryx + lyx ) / ( lyx - ryx ) ;
ret [ 1 ] = ( ret [ 0 ] + pconf . twopoint_param ) * lyx ;
2019-07-12 21:10:01 +00:00
ret [ 2 ] = 0 ;
2022-03-27 13:30:22 +00:00
ret [ 0 ] = - ret [ 0 ] ; ret [ 1 ] = - ret [ 1 ] ;
2019-07-12 21:10:01 +00:00
move_y_to_z ( ret , yz ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2019-07-12 21:10:01 +00:00
break ;
}
case mdTwoHybrid : {
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2019-07-12 21:10:01 +00:00
auto yz = move_z_to_y ( H ) ;
ret = compute_hybrid ( H , whateveri [ 0 ] ) ;
move_y_to_z ( ret , yz ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2019-07-12 21:10:01 +00:00
break ;
}
2018-11-06 14:53:50 +00:00
case mdJoukowsky :
case mdJoukowskyInverted : {
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
// with equal speed skiprope: models::scr_to_orientation(H[1], H[0]);
2018-11-06 14:53:50 +00:00
2020-04-16 22:53:58 +00:00
if ( pconf . skiprope ) {
2018-11-06 14:53:50 +00:00
static ld last_skiprope = 0 ;
static transmatrix lastmatrix ;
2020-04-16 22:53:58 +00:00
if ( pconf . skiprope ! = last_skiprope ) {
ret = mobius ( C0 , - pconf . skiprope , 2 ) ;
2018-11-06 14:53:50 +00:00
const cld c1 ( 1 , 0 ) ;
const cld c2 ( 2 , 0 ) ;
const cld c4 ( 4 , 0 ) ;
cld w ( ret [ 0 ] , ret [ 1 ] ) ;
cld z = sqrt ( c4 * w * w - c1 ) + c2 * w ;
if ( abs ( z ) > 1 ) z = c1 / z ;
2019-02-22 19:58:40 +00:00
hyperpoint zr = hpxyz ( real ( z ) , imag ( z ) , 0 ) ;
2018-11-06 14:53:50 +00:00
hyperpoint inhyp = perspective_to_space ( zr , 1 , gcHyperbolic ) ;
2020-04-16 22:53:58 +00:00
last_skiprope = pconf . skiprope ;
2018-11-06 14:53:50 +00:00
lastmatrix = rgpushxto0 ( inhyp ) ;
}
H = lastmatrix * H ;
}
H = space_to_perspective ( H ) ;
2019-03-20 01:10:53 +00:00
auto yz = move_z_to_y ( H ) ;
2019-02-27 18:33:13 +00:00
ld r = hypot_d ( 2 , H ) ;
2018-11-06 14:53:50 +00:00
ld c = H [ 0 ] / r ;
ld s = H [ 1 ] / r ;
2020-04-16 22:53:58 +00:00
ld & mt = pconf . model_transition ;
2018-11-06 14:53:50 +00:00
ld a = 1 - .5 * mt , b = .5 * mt ;
swap ( a , b ) ;
2018-10-26 19:03:27 +00:00
2018-11-06 14:53:50 +00:00
ret [ 0 ] = ( a * r + b / r ) * c / 2 ;
ret [ 1 ] = ( a * r - b / r ) * s / 2 ;
ret [ 2 ] = 0 ;
2020-04-16 22:53:58 +00:00
if ( pconf . skiprope )
ret = mobius ( ret , pconf . skiprope , 2 ) ;
2019-03-20 01:10:53 +00:00
2018-11-06 14:53:50 +00:00
if ( pmodel = = mdJoukowskyInverted ) {
2019-02-27 18:33:13 +00:00
ld r2 = sqhypot_d ( 2 , ret ) ;
2022-03-27 12:32:29 +00:00
if ( pconf . dualfocus_autoscale )
ret * = ( 1 - pconf . model_transition ) / 2 ;
2018-11-06 14:53:50 +00:00
ret [ 0 ] = ret [ 0 ] / r2 ;
ret [ 1 ] = - ret [ 1 ] / r2 ;
2019-03-20 01:10:53 +00:00
move_y_to_z ( ret , yz ) ;
2018-11-06 14:53:50 +00:00
/*
ret [ 0 ] + = 1 ;
ld alpha = atan2 ( ret [ 1 ] , ret [ 0 ] ) ;
ld mod = hypot ( ret [ 0 ] , ret [ 1 ] ) ;
// ret[0] = cos(alpha/2) * sqrt(mod);
// ret[1] = sin(alpha/2) * sqrt(mod);
ret [ 0 ] = alpha ;
ret [ 1 ] = log ( mod ) ; */
}
2019-03-20 01:10:53 +00:00
else {
2023-08-08 14:27:52 +00:00
move_y_to_z ( ret , yz ) ;
2019-03-20 01:10:53 +00:00
}
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2018-11-06 14:53:50 +00:00
break ;
2018-10-25 00:44:35 +00:00
}
2018-11-06 14:53:50 +00:00
case mdPolygonal : case mdPolynomial : {
2018-03-26 17:06:47 +00:00
2018-11-06 14:53:50 +00:00
H = space_to_perspective ( H ) ;
2018-10-23 14:58:19 +00:00
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2018-11-06 14:53:50 +00:00
pair < long double , long double > p = polygonal : : compute ( H [ 0 ] , H [ 1 ] ) ;
ret [ 0 ] = p . first ;
ret [ 1 ] = p . second ;
ret [ 2 ] = 0 ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2018-11-06 14:53:50 +00:00
break ;
}
case mdBand :
2020-04-16 22:53:58 +00:00
if ( pconf . model_transition ! = 1 ) {
2023-01-04 22:30:54 +00:00
H = unshift ( H_orig ) ;
2020-04-16 22:53:58 +00:00
ld & mt = pconf . model_transition ;
2018-11-06 14:53:50 +00:00
H = space_to_perspective ( H ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2018-11-06 14:53:50 +00:00
H [ 0 ] + = 1 ;
double rad = H [ 0 ] * H [ 0 ] + H [ 1 ] * H [ 1 ] ;
H [ 1 ] / = rad ;
H [ 0 ] / = rad ;
H [ 0 ] - = .5 ;
ld phi = atan2 ( H ) ;
2019-02-27 18:33:13 +00:00
ld r = hypot_d ( 2 , H ) ;
2018-11-06 14:53:50 +00:00
r = pow ( r , 1 - mt ) ;
phi * = ( 1 - mt ) ;
ret [ 0 ] = r * cos ( phi ) ;
ret [ 1 ] = r * sin ( phi ) ;
ret [ 2 ] = 0 ;
ret [ 0 ] - = pow ( 0.5 , 1 - mt ) ;
2022-11-12 21:38:45 +00:00
ret [ 0 ] / = - ( 1 - mt ) * 90. _deg ;
ret [ 1 ] / = ( 1 - mt ) * 90. _deg ;
2018-11-06 14:53:50 +00:00
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2018-11-06 14:53:50 +00:00
}
else
2020-07-27 16:49:04 +00:00
makeband ( H_orig , ret , band_conformal ) ;
2018-11-06 14:53:50 +00:00
break ;
2020-09-15 17:17:07 +00:00
case mdMiller :
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) {
2020-09-16 12:48:23 +00:00
y * = pconf . miller_parameter ;
2020-09-15 17:17:07 +00:00
band_conformal ( x , y ) ;
2020-09-16 12:48:23 +00:00
y / = pconf . miller_parameter ;
2020-09-15 17:17:07 +00:00
} ) ;
break ;
case mdLoximuthal :
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) {
ld orig_y = y ;
band_conformal ( x , y ) ;
2020-09-16 12:48:39 +00:00
ld x0 = 0 , y0 = pconf . loximuthal_parameter ; band_conformal ( x0 , y0 ) ;
y - = y0 ;
2020-09-15 17:17:07 +00:00
orig_y - = pconf . loximuthal_parameter ;
2020-09-16 12:48:39 +00:00
2020-09-15 17:17:07 +00:00
if ( y ) x = x * orig_y / y ;
y = orig_y ;
} ) ;
break ;
2018-11-06 14:53:50 +00:00
case mdTwoPoint :
2020-07-27 16:49:04 +00:00
makeband ( H_orig , ret , make_twopoint ) ;
2018-11-06 14:53:50 +00:00
break ;
2021-03-06 10:54:25 +00:00
case mdThreePoint :
2021-03-30 09:27:48 +00:00
# if MAXMDIM >= 4
2021-03-06 10:54:25 +00:00
threepoint_projection ( H , ret ) ;
2021-03-30 09:27:48 +00:00
# else
throw hr_exception ( ) ;
# endif
2021-03-06 10:54:25 +00:00
break ;
2019-08-14 06:23:09 +00:00
case mdMollweide :
2020-07-27 16:49:04 +00:00
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) {
2019-08-14 06:23:09 +00:00
ld theta =
hyperbolic ? min ( y / 2 + 0.572365 , y * 0.78509 ) :
euclid ? y :
2022-11-12 21:38:45 +00:00
y > 0 ? max ( y * 0.012 / 0.015 , 90. _deg - ( 90. _deg - y ) * 0.066262 / 0.015708 ) :
min ( y * 0.012 / 0.015 , - 90. _deg + ( 90. _deg + y ) * 0.066262 / 0.015708 ) ;
2019-08-14 06:23:09 +00:00
2022-11-12 21:38:45 +00:00
if ( sphere & & abs ( theta ) > = 90. _deg - 1e-6 ) ;
2019-08-14 06:23:09 +00:00
else {
for ( int it = 0 ; it < 4 ; it + + ) {
auto a = ( sin_auto ( 2 * theta ) + 2 * theta - M_PI * sin_auto ( y ) ) ;
auto b = ( 2 + 2 * cos_auto ( 2 * theta ) ) ;
theta = theta - a / b ;
} }
2022-11-12 21:38:45 +00:00
y = 90. _deg * sin_auto ( theta ) ;
2019-08-14 06:23:09 +00:00
x = x * cos_auto ( theta ) ;
} ) ;
break ;
2019-08-14 15:13:20 +00:00
case mdCentralCyl :
2020-07-27 16:49:04 +00:00
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) { y = tan_auto ( y ) ; ld top = vid . yres * M_PI / current_display - > radius ; if ( y > top ) y = top ; if ( y < - top ) y = - top ; } ) ;
2019-08-14 15:13:20 +00:00
break ;
2020-09-15 17:17:07 +00:00
case mdGallStereographic :
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) {
y = 2 * sin_auto ( y ) / ( 1 + cos_auto ( y ) ) ;
ld top = vid . yres * M_PI / current_display - > radius ; if ( y > top ) y = top ; if ( y < - top ) y = - top ;
} ) ;
break ;
case mdAitoff : case mdHammer : case mdWinkelTripel :
makeband ( H_orig , ret , [ & ] ( ld & x , ld & y ) {
ld ox = x , oy = y ;
x * = pconf . aitoff_parameter ;
ld x0 = sin_auto ( x ) * cos_auto ( y ) ;
ld y0 = cos_auto ( x ) * cos_auto ( y ) ;
ld z0 = sin_auto ( y ) ;
ld d = acos_auto ( y0 ) ;
ld d0 = hypot ( x0 , z0 ) ;
if ( md = = mdAitoff | | md = = mdWinkelTripel ) ;
2022-11-12 21:38:45 +00:00
else if ( sphere ) d = sqrt ( 2 * ( 1 - cos ( d ) ) ) * 90. _deg ;
2020-09-15 17:17:07 +00:00
else d = sqrt ( 2 * ( cosh ( d ) - 1 ) ) / 1.5 ;
x = x0 * d / d0 / pconf . aitoff_parameter , y = z0 * d / d0 ;
if ( md = = mdWinkelTripel )
2020-09-16 12:48:46 +00:00
x = lerp ( x , ox , pconf . winkel_parameter ) ,
y = lerp ( y , oy , pconf . winkel_parameter ) ;
2020-09-15 17:17:07 +00:00
} ) ;
break ;
case mdWerner : {
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2020-09-15 17:17:07 +00:00
find_zlev ( H ) ; // ignored for now
ld r = hdist0 ( H ) ;
if ( r = = 0 ) { ret = H ; return ; }
ld angle = atan2 ( H [ 0 ] , H [ 1 ] ) ;
angle * = sin_auto ( r ) / r ;
ret [ 0 ] = sin ( angle ) * r ;
ret [ 1 ] = cos ( angle ) * r ;
ret [ 2 ] = 0 ;
ret [ 3 ] = 1 ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2020-09-15 17:17:07 +00:00
break ;
}
2019-08-14 15:13:20 +00:00
case mdCollignon :
2020-07-27 16:49:04 +00:00
find_zlev ( H_orig . h ) ;
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) {
2019-08-14 16:59:21 +00:00
ld sgn = 1 ;
2020-04-16 22:53:58 +00:00
if ( pconf . collignon_reflected & & y > 0 ) y = - y , sgn = - 1 ;
y = signed_sqrt ( sin_auto ( y ) + pconf . collignon_parameter ) ;
2019-08-14 16:59:21 +00:00
x * = y / 1.2 ;
2020-04-16 22:53:58 +00:00
y - = signed_sqrt ( pconf . collignon_parameter ) ;
2019-08-14 16:59:21 +00:00
y * = sgn ;
y * = M_PI ;
2019-08-14 15:13:20 +00:00
} ) ;
break ;
2018-11-06 14:53:50 +00:00
case mdBandEquiarea :
2020-07-27 16:49:04 +00:00
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) { y = sin_auto ( y ) ; } ) ;
2018-11-06 14:53:50 +00:00
break ;
2018-10-23 14:58:19 +00:00
2018-11-06 14:53:50 +00:00
case mdBandEquidistant :
2020-07-27 16:49:04 +00:00
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) { } ) ;
2018-11-06 14:53:50 +00:00
break ;
2018-10-23 14:58:19 +00:00
2018-11-06 14:53:50 +00:00
case mdSinusoidal :
2020-07-27 16:49:04 +00:00
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) { x * = cos_auto ( y ) ; } ) ;
2018-11-06 14:53:50 +00:00
break ;
2018-03-26 17:06:47 +00:00
2024-02-22 00:16:43 +00:00
case mdPolar : {
models : : scr_to_ori ( H ) ;
H = xpush ( pconf . offside ) * H ;
ld zlev = find_zlev ( H ) ;
ld d = hdist0 ( H ) ;
ld df , zf ;
hypot_zlev ( zlev , d , df , zf ) ;
ret [ 0 ] = - atan2 ( H ) / M_PI ;
ret [ 1 ] = ( d - pconf . offside2 ) / M_PI ;
ret [ 2 ] = zf ;
ret [ 3 ] = 1 ;
models : : ori_to_scr ( ret ) ;
break ;
}
2019-03-30 16:45:56 +00:00
case mdEquidistant : case mdEquiarea : case mdEquivolume : {
2020-12-31 15:35:31 +00:00
if ( vrhr : : rendering ( ) & & GDIM = = 3 & & pmodel = = mdEquidistant ) {
2020-12-31 14:02:10 +00:00
ret = inverse_exp ( H_orig ) ;
ret [ 3 ] = 1 ;
return ;
}
2022-12-08 18:38:06 +00:00
if ( nonisotropic | | gproduct ) {
2020-07-27 16:49:04 +00:00
ret = lp_apply ( inverse_exp ( H_orig ) ) ;
2019-08-24 16:14:38 +00:00
ret [ 3 ] = 1 ;
2019-08-14 18:44:31 +00:00
break ;
}
2018-11-06 14:53:50 +00:00
ld zlev = find_zlev ( H ) ;
2019-08-15 13:05:43 +00:00
ld rad = hypot_d ( GDIM , H ) ;
2018-11-06 14:53:50 +00:00
if ( rad = = 0 ) rad = 1 ;
ld d = hdist0 ( H ) ;
ld df , zf ;
hypot_zlev ( zlev , d , df , zf ) ;
2022-02-01 16:09:20 +00:00
if ( md = = mdEquivolume )
2022-11-12 21:38:45 +00:00
d = pow ( volume_auto ( d ) , 1 / 3. ) * pow ( 90. _deg , 1 / 3. ) ;
2022-02-01 16:09:20 +00:00
else if ( md = = mdEquiarea & & sphere ) {
2022-11-12 21:38:45 +00:00
d = sqrt ( 2 * ( 1 - cos ( d ) ) ) * 90. _deg ;
2022-02-01 16:09:20 +00:00
}
2018-11-06 14:53:50 +00:00
else if ( pmodel = = mdEquiarea & & hyperbolic )
d = sqrt ( 2 * ( cosh ( d ) - 1 ) ) / 1.5 ;
2020-12-30 19:21:07 +00:00
ld factor = d * df / rad ;
if ( ! vrhr : : rendering ( ) ) factor / = M_PI ;
2018-11-06 14:53:50 +00:00
2020-12-30 19:21:07 +00:00
ret = H * factor ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 2 ) ret [ 2 ] = 0 ;
2019-03-20 01:10:53 +00:00
if ( MAXMDIM = = 4 ) ret [ 3 ] = 1 ;
2020-12-27 16:14:50 +00:00
if ( zlev ! = 1 & & use_z_coordinate ( ) )
2018-11-06 14:53:50 +00:00
apply_depth ( ret , d * zf / M_PI ) ;
break ;
2018-03-25 13:27:42 +00:00
}
2018-11-06 14:53:50 +00:00
2018-11-06 23:52:48 +00:00
case mdRotatedHyperboles : {
// ld zlev = <- not implemented
2019-05-29 14:27:24 +00:00
find_zlev ( H ) ; // + vid.depth;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2018-11-06 23:52:48 +00:00
ld y = asin_auto ( H [ 1 ] ) ;
ld x = asin_auto_clamp ( H [ 0 ] / cos_auto ( y ) ) ;
// ld z = zlev == 1 ? 0 : geom3::factor_to_lev(zlev);
2019-05-29 14:27:24 +00:00
ld factor = geom3 : : lev_to_factor ( y + vid . depth ) ;
2018-11-06 23:52:48 +00:00
ret [ 0 ] = sinh ( x ) * factor ;
ret [ 1 ] = cosh ( x ) * factor ;
ret [ 2 ] = 0 ;
2020-04-16 22:53:58 +00:00
if ( pconf . use_atan ) {
2018-12-24 00:10:55 +00:00
ret [ 0 ] = atan ( ret [ 0 ] ) ;
ret [ 1 ] = atan ( ret [ 1 ] ) ;
}
2018-11-06 23:52:48 +00:00
break ;
}
case mdFormula : {
2020-04-16 22:53:58 +00:00
dynamicval < eModel > m ( pmodel , pconf . basic_model ) ;
2020-07-27 16:49:04 +00:00
applymodel ( H_orig , ret ) ;
2018-11-06 23:52:48 +00:00
exp_parser ep ;
ep . extra_params [ " z " ] = cld ( ret [ 0 ] , ret [ 1 ] ) ;
ep . extra_params [ " cx " ] = ret [ 0 ] ;
ep . extra_params [ " cy " ] = ret [ 1 ] ;
ep . extra_params [ " cz " ] = ret [ 2 ] ;
2018-11-07 06:21:42 +00:00
ep . extra_params [ " ux " ] = H [ 0 ] ;
ep . extra_params [ " uy " ] = H [ 1 ] ;
ep . extra_params [ " uz " ] = H [ 2 ] ;
2020-04-16 22:53:58 +00:00
ep . s = pconf . formula ;
2019-12-23 20:44:51 +00:00
cld res ;
try {
res = ep . parse ( ) ;
}
catch ( hr_parse_exception & ) {
res = 0 ;
}
2018-11-06 23:52:48 +00:00
ret [ 0 ] = real ( res ) ;
ret [ 1 ] = imag ( res ) ;
ret [ 2 ] = 0 ;
break ;
}
2018-11-09 13:15:00 +00:00
case mdSpiral : {
2018-11-10 15:56:08 +00:00
cld z ;
2024-05-16 19:16:10 +00:00
if ( hyperbolic | | sphere ) {
makeband ( H_orig , ret , band_conformal ) ;
models : : scr_to_ori ( ret ) ;
}
2018-11-10 15:56:08 +00:00
else ret = H ;
2019-08-09 22:58:50 +00:00
z = cld ( ret [ 0 ] , ret [ 1 ] ) * models : : spiral_multiplier ;
2018-12-21 13:42:59 +00:00
2020-04-16 22:53:58 +00:00
if ( pconf . spiral_cone < 360 ) {
ld alpha = imag ( z ) * 360 / pconf . spiral_cone ;
2018-12-21 13:42:59 +00:00
ld r = real ( z ) ;
r = exp ( r ) ;
ret [ 0 ] = - sin ( alpha ) * r ;
ret [ 1 ] = cos ( alpha ) * r ;
2019-11-28 19:16:17 +00:00
if ( euclid ) ret = models : : euclidean_spin * ret ;
2020-04-16 22:53:58 +00:00
ret [ 2 ] = ( r - 1 ) * sqrt ( pow ( 360 / pconf . spiral_cone , 2 ) - 1 ) ;
2018-12-21 13:42:59 +00:00
2023-08-14 14:18:44 +00:00
ret = pconf . ball ( ) * ret ;
2018-12-21 13:42:59 +00:00
}
else {
z = exp ( z ) ;
ret [ 0 ] = real ( z ) ;
ret [ 1 ] = imag ( z ) ;
2019-11-28 19:16:17 +00:00
if ( euclid ) ret = models : : euclidean_spin * ret ;
2018-11-09 13:15:00 +00:00
2020-04-16 22:53:58 +00:00
if ( pconf . skiprope )
ret = mobius ( ret , pconf . skiprope , 1 ) ;
2018-12-21 13:42:59 +00:00
}
2024-05-16 19:16:10 +00:00
models : : ori_to_scr ( ret ) ;
2021-03-12 14:31:17 +00:00
break ;
2018-11-09 13:15:00 +00:00
}
2020-10-08 16:22:28 +00:00
case mdRetroCraig : {
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) {
if ( x )
y = x / sin_auto ( x ) * ( sin_auto ( y ) * cos_auto ( x ) - tan_auto ( pconf . loximuthal_parameter ) * cos_auto ( y ) ) ;
else
y = sin_auto ( y ) - tan_auto ( pconf . loximuthal_parameter ) * cos_auto ( y ) ;
} ) ;
break ;
}
case mdRetroLittrow : {
makeband ( H_orig , ret , [ ] ( ld & x , ld & y ) {
tie ( x , y ) = make_pair (
sin_auto ( x ) / cos_auto ( y ) ,
cos_auto ( x ) * tan_auto ( y )
) ;
} ) ;
break ;
}
case mdRetroHammer : {
ld d = hdist ( H , ypush0 ( pconf . loximuthal_parameter ) ) ;
makeband ( H_orig , ret , [ d , H ] ( ld & x , ld & y ) {
if ( x = = 0 & & y = = 0 ) return ;
if ( x )
y = x / sin_auto ( x ) * ( sin_auto ( y ) * cos_auto ( x ) - tan_auto ( pconf . loximuthal_parameter ) * cos_auto ( y ) ) ;
else
y = sin_auto ( y ) - tan_auto ( pconf . loximuthal_parameter ) * cos_auto ( y ) ;
ld scale = d / hypot ( x , y ) ;
if ( H [ 2 ] < 0 ) scale = - scale ;
x * = scale ;
y * = scale ;
} ) ;
break ;
}
case mdPanini : {
2022-03-27 12:32:29 +00:00
find_zlev ( H ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2022-03-27 12:32:29 +00:00
2020-10-08 16:22:28 +00:00
ld proh = sqrt ( H [ 2 ] * H [ 2 ] + curvature ( ) * H [ 0 ] * H [ 0 ] ) ;
H / = proh ;
H / = ( H [ 2 ] + pconf . alpha ) ;
ret = H ;
ret [ 2 ] = 0 ; ret [ 3 ] = 1 ;
2022-03-27 12:32:29 +00:00
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2020-10-08 16:22:28 +00:00
break ;
}
case mdPoorMan : {
find_zlev ( H ) ;
H = space_to_perspective ( H ) ;
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2020-10-08 16:22:28 +00:00
ld u = H [ 0 ] , v = H [ 1 ] ;
if ( abs ( u ) > 1e-3 & & abs ( v ) > 1e-3 ) {
ld r2 = u * u + v * v ;
ld scale = sqrt ( ( - r2 + sqrt ( r2 * ( r2 + 4 * u * u * v * v * ( r2 - 2 ) ) ) ) / ( 2 * ( r2 - 2 ) ) ) / u / v ;
if ( u * v < 0 ) scale = - scale ;
H = scale * H ;
}
ret = H ;
ret [ 2 ] = 0 ;
ret [ 3 ] = 1 ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( ret ) ;
2020-10-08 16:22:28 +00:00
break ;
}
2019-10-21 20:34:20 +00:00
case mdGUARD : case mdManual : break ;
2022-03-27 07:05:47 +00:00
default :
if ( md < isize ( extra_projections ) & & extra_projections [ md ] )
extra_projections [ md ] ( H_orig , H , ret ) ;
break ;
2017-07-10 18:47:38 +00:00
}
2018-11-06 14:53:50 +00:00
2019-02-06 21:41:45 +00:00
ghcheck ( ret , H_orig ) ;
2017-07-10 18:47:38 +00:00
}
// game-related graphics
2019-08-09 19:00:52 +00:00
EX transmatrix sphereflip ; // on the sphere, flip
EX bool playerfound ; // has player been found in the last drawing?
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX bool outofmap ( hyperpoint h ) {
2019-05-09 15:02:50 +00:00
if ( GDIM = = 3 )
return false ;
else if ( euclid )
2019-05-26 16:04:02 +00:00
return h [ 2 ] < .5 ; // false; // h[0] * h[0] + h[1] * h[1] > 15 * cgi.crossf;
2017-07-10 18:47:38 +00:00
else if ( sphere )
return h [ 2 ] < .1 & & h [ 2 ] > - .1 & & h [ 1 ] > - .1 & & h [ 1 ] < .1 & & h [ 0 ] > - .1 & & h [ 0 ] < .1 ;
else
return h [ 2 ] < .5 ;
}
2019-08-09 21:39:36 +00:00
EX hyperpoint mirrorif ( const hyperpoint & V , bool b ) {
2017-07-10 18:47:38 +00:00
if ( b ) return Mirror * V ;
else return V ;
}
2020-07-27 16:49:04 +00:00
EX shiftmatrix mirrorif ( const shiftmatrix & V , bool b ) {
2017-07-16 21:00:55 +00:00
if ( b ) return V * Mirror ;
else return V ;
}
2017-07-10 18:47:38 +00:00
// -1 if away, 0 if not away
2019-09-06 06:17:02 +00:00
EX int away ( const transmatrix & V2 ) {
2017-10-06 22:33:32 +00:00
return ( intval ( C0 , V2 * xpush0 ( .1 ) ) > intval ( C0 , tC0 ( V2 ) ) ) ? - 1 : 0 ;
2017-07-10 18:47:38 +00:00
}
/* double zgrad(double f1, double f2, int nom, int den) {
using namespace geom3 ;
ld fo1 = factor_to_lev ( f1 ) ;
ld fo2 = factor_to_lev ( f2 ) ;
return lev_to_factor ( fo1 + ( fo2 - fo1 ) * nom / den ) ;
} */
2019-09-06 06:17:02 +00:00
EX double zgrad0 ( double l1 , double l2 , int nom , int den ) {
2017-07-10 18:47:38 +00:00
using namespace geom3 ;
return lev_to_factor ( l1 + ( l2 - l1 ) * nom / den ) ;
}
2019-08-09 19:00:52 +00:00
EX bool behindsphere ( const hyperpoint & h ) {
2017-07-10 18:47:38 +00:00
if ( ! sphere ) return false ;
2018-04-22 09:11:47 +00:00
if ( mdBandAny ( ) ) return false ;
2020-04-16 22:53:58 +00:00
if ( pconf . alpha > 1 ) {
if ( h [ LDIM ] > - 1 / pconf . alpha ) return true ;
2017-07-10 18:47:38 +00:00
}
2020-04-16 22:53:58 +00:00
if ( pconf . alpha < = 1 ) {
if ( h [ LDIM ] < .2 - pconf . alpha ) return true ;
2017-07-10 18:47:38 +00:00
}
return false ;
}
2017-10-09 09:46:49 +00:00
ld to01 ( ld a0 , ld a1 , ld x ) {
if ( x < a0 ) return 0 ;
if ( x > a1 ) return 1 ;
return ( x - a0 ) / ( a1 - a0 ) ;
}
2019-09-06 06:17:02 +00:00
EX ld spherity ( const hyperpoint & h ) {
2017-10-09 09:46:49 +00:00
if ( ! sphere ) return 1 ;
2020-04-16 22:53:58 +00:00
if ( pconf . alpha > 1 ) {
return to01 ( 1 / pconf . alpha , 1 , abs ( h [ 2 ] ) ) ;
2017-10-09 09:46:49 +00:00
}
2020-04-16 22:53:58 +00:00
if ( pconf . alpha < = 1 ) {
2017-11-03 18:20:54 +00:00
return to01 ( - 1.5 , 1 , h [ 2 ] ) ;
2017-10-09 09:46:49 +00:00
}
return 1 ;
}
2019-08-09 19:00:52 +00:00
EX bool behindsphere ( const transmatrix & V ) {
2017-07-10 18:47:38 +00:00
return behindsphere ( tC0 ( V ) ) ;
}
2020-07-27 16:49:04 +00:00
EX bool behindsphere ( const shiftmatrix & V ) {
return behindsphere ( tC0 ( V . T ) ) ;
}
2019-09-06 06:17:02 +00:00
EX ld spherity ( const transmatrix & V ) {
2017-10-09 09:46:49 +00:00
return spherity ( tC0 ( V ) ) ;
}
2019-08-09 20:07:03 +00:00
EX bool confusingGeometry ( ) {
2020-05-31 16:04:43 +00:00
# if MAXMDIM >= 4
2020-05-31 15:21:16 +00:00
if ( reg3 : : ultra_mirror_in ( ) ) return true ;
# endif
2024-06-29 10:41:39 +00:00
if ( mproduct | | mtwisted ) return ( hybrid : : csteps & & ! PIU ( fake : : in ( ) & & ! fake : : multiple ) ) | | PIU ( confusingGeometry ( ) ) ;
2020-05-31 15:21:16 +00:00
return quotient | | elliptic | | ( fake : : in ( ) & & fake : : multiple ) ;
2017-07-10 18:47:38 +00:00
}
2019-08-09 20:07:03 +00:00
EX ld master_to_c7_angle ( ) {
2022-12-13 18:04:43 +00:00
if ( dont_inverse ( ) ) return 0 ;
2022-12-08 18:38:06 +00:00
if ( mhybrid ) return hybrid : : in_underlying_geometry ( master_to_c7_angle ) ;
2019-11-29 13:59:54 +00:00
if ( WDIM = = 3 ) return 0 ;
2019-05-26 16:04:02 +00:00
ld alpha = 0 ;
2019-02-17 17:28:20 +00:00
# if CAP_GP
2019-05-26 16:04:02 +00:00
if ( cgi . gpdata ) alpha = cgi . gpdata - > alpha ;
2019-02-17 17:28:20 +00:00
# endif
2019-12-14 11:05:01 +00:00
return ( ! BITRUNCATED & & ! bt : : in ( ) & & ! arcm : : in ( ) ) ? M_PI + alpha : 0 ;
2018-04-10 15:06:04 +00:00
}
2019-08-09 20:07:03 +00:00
EX transmatrix actualV ( const heptspin & hs , const transmatrix & V ) {
2022-12-08 18:38:06 +00:00
if ( gproduct ) return PIU ( actualV ( hs , V ) ) ;
2019-05-08 16:33:08 +00:00
if ( WDIM = = 3 ) return V ;
2019-02-17 17:28:20 +00:00
# if CAP_IRR
2018-08-28 15:17:34 +00:00
if ( IRREGULAR )
2022-11-12 21:38:45 +00:00
return V * spin ( M_PI + TAU / S7 * ( hs . spin + irr : : periodmap [ hs . at ] . base . spin ) ) ;
2019-02-17 17:28:20 +00:00
# endif
# if CAP_ARCM
2019-12-14 10:42:16 +00:00
if ( arcm : : in ( ) ) return V * spin ( - arcm : : current . triangles [ arcm : : id_of ( hs . at ) ] [ hs . spin ] . first ) ;
2019-02-17 17:28:20 +00:00
# endif
# if CAP_BT
2019-12-14 11:05:01 +00:00
if ( bt : : in ( ) ) return V ;
2019-02-17 17:28:20 +00:00
# endif
2019-12-14 11:12:24 +00:00
if ( kite : : in ( ) ) return V ;
2022-11-12 21:38:45 +00:00
return ( hs . spin | | ! BITRUNCATED ) ? V * spin ( hs . spin * TAU / hs . at - > type + master_to_c7_angle ( ) ) : V ;
2017-12-16 08:03:50 +00:00
}
2020-07-27 16:49:04 +00:00
EX shiftmatrix actualV ( const heptspin & hs , const shiftmatrix & V ) {
return shiftless ( actualV ( hs , V . T ) , V . shift ) ;
}
EX bool point_behind ( const shiftpoint h ) {
2019-07-31 14:14:01 +00:00
if ( sphere ) return false ;
if ( ! in_perspective ( ) ) return false ;
2020-07-27 16:49:04 +00:00
hyperpoint h1 ;
if ( pmodel = = mdGeodesic ) h1 = inverse_exp ( h , pQUICK ) ;
2022-12-08 18:38:06 +00:00
if ( pmodel = = mdPerspective & & gproduct ) h1 = product : : inverse_exp ( h . h ) ;
2020-07-27 16:49:04 +00:00
h1 = lp_apply ( h1 ) ;
return h1 [ 2 ] < 1e-8 ;
2019-02-21 17:46:53 +00:00
}
2019-05-29 18:21:19 +00:00
void raise_error ( ) {
println ( hlog , " something wrong " ) ;
}
2019-08-09 19:00:52 +00:00
EX bool invalid_matrix ( const transmatrix T ) {
2019-08-15 13:05:43 +00:00
for ( int i = 0 ; i < GDIM ; i + + ) for ( int j = 0 ; j < GDIM ; j + + )
2019-05-29 18:21:19 +00:00
if ( std : : isnan ( T [ i ] [ j ] ) | | T [ i ] [ j ] > 1e8 | | T [ i ] [ j ] < - 1e8 | | std : : isinf ( T [ i ] [ j ] ) )
return true ;
2022-12-08 18:38:06 +00:00
if ( gproduct | | ( cgflags & qAFFINE ) ) {
2019-12-06 12:06:32 +00:00
for ( int i = 0 ; i < GDIM ; i + + ) for ( int j = 0 ; j < GDIM ; j + + ) if ( abs ( T [ i ] [ j ] ) > 1e-60 ) return false ;
2019-08-17 21:28:41 +00:00
}
else
for ( int i = 0 ; i < GDIM ; i + + ) for ( int j = 0 ; j < GDIM ; j + + ) if ( T [ i ] [ j ] > .5 | | T [ i ] [ j ] < - .5 ) return false ;
2019-05-29 18:21:19 +00:00
return true ;
}
2019-08-09 19:00:52 +00:00
EX bool invalid_point ( const hyperpoint h ) {
2019-08-17 21:28:41 +00:00
return std : : isnan ( h [ LDIM ] ) | | h [ LDIM ] > 1e8 | | std : : isinf ( h [ LDIM ] ) ;
2018-12-04 21:40:29 +00:00
}
2020-07-27 16:49:04 +00:00
EX bool invalid_point ( const shiftpoint h ) { return invalid_point ( h . h ) ; }
EX bool in_smart_range ( const shiftmatrix & T ) {
shiftpoint h = tC0 ( T ) ;
2019-07-31 14:14:01 +00:00
if ( invalid_point ( h ) ) return false ;
2019-10-01 03:03:46 +00:00
if ( nil | | nih ) return true ;
2019-09-13 17:50:12 +00:00
# if CAP_SOLV
2022-04-16 11:59:38 +00:00
if ( pmodel = = mdGeodesic ) return nisot : : in_table_range ( h . h ) ;
2019-09-13 17:50:12 +00:00
# endif
2019-03-21 18:13:45 +00:00
hyperpoint h1 ;
2019-07-31 14:14:01 +00:00
applymodel ( h , h1 ) ;
if ( invalid_point ( h1 ) ) return false ;
2018-11-17 18:24:02 +00:00
ld x = current_display - > xcenter + current_display - > radius * h1 [ 0 ] ;
2020-04-16 22:53:58 +00:00
ld y = current_display - > ycenter + current_display - > radius * h1 [ 1 ] * pconf . stretch ;
2018-12-01 22:48:21 +00:00
2022-12-29 20:49:43 +00:00
bool inp = in_perspective ( ) ;
2022-12-08 18:38:06 +00:00
2023-02-04 18:44:06 +00:00
if ( frustum_culling ) {
2022-12-08 18:38:06 +00:00
if ( x > current_display - > xtop + current_display - > xsize * 2 ) return false ;
if ( x < current_display - > xtop - current_display - > xsize * 1 ) return false ;
if ( y > current_display - > ytop + current_display - > ysize * 2 ) return false ;
if ( y < current_display - > ytop - current_display - > ysize * 1 ) return false ;
2022-12-29 20:49:43 +00:00
if ( GDIM = = 3 & & ! inp ) {
2022-12-08 18:38:06 +00:00
if ( - h1 [ 2 ] < pconf . clip_min * 2 - pconf . clip_max ) return false ;
if ( - h1 [ 2 ] > pconf . clip_max * 2 - pconf . clip_min ) return false ;
}
2019-03-21 18:13:45 +00:00
}
2018-12-01 22:48:21 +00:00
2018-11-01 17:59:25 +00:00
ld epsilon = 0.01 ;
2019-03-21 18:13:45 +00:00
2020-08-20 14:12:04 +00:00
transmatrix ar ;
2019-03-21 18:13:45 +00:00
ld dx = 0 , dy = 0 , dz = 0 , dh [ MAXMDIM ] ;
2019-08-15 13:05:43 +00:00
for ( int i = 0 ; i < GDIM ; i + + ) {
2019-03-21 18:13:45 +00:00
hyperpoint h2 ;
applymodel ( T * cpush0 ( i , epsilon ) , h2 ) ;
ld x1 = current_display - > radius * abs ( h2 [ 0 ] - h1 [ 0 ] ) / epsilon ;
2020-04-16 22:53:58 +00:00
ld y1 = current_display - > radius * abs ( h2 [ 1 ] - h1 [ 1 ] ) * pconf . stretch / epsilon ;
2020-08-20 14:12:04 +00:00
for ( int j = 0 ; j < GDIM ; j + + ) ar [ i ] [ j ] = current_display - > radius * ( h2 [ j ] - h1 [ j ] ) / epsilon ;
2019-03-21 18:13:45 +00:00
dx = max ( dx , x1 ) ; dy = max ( dy , y1 ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) dz = max ( dz , abs ( h2 [ 2 ] - h1 [ 2 ] ) ) ;
2019-03-21 18:13:45 +00:00
dh [ i ] = hypot ( x1 , y1 ) ;
}
2020-08-20 14:12:04 +00:00
if ( GDIM = = 2 & & vid . smart_area_based ) {
ld area = det2 ( ar ) ;
ld scale = sqrt ( area ) * cgi . scalefactor * hcrossf7 ;
if ( scale < = vid . smart_range_detail ) return false ;
}
else if ( GDIM = = 3 ) {
2022-12-29 20:49:43 +00:00
if ( ! inp & & ( - h1 [ 2 ] + 2 * dz < pconf . clip_min | | - h1 [ 2 ] - 2 * dz > pconf . clip_max ) ) return false ;
sort ( dh , dh + 3 ) ;
2019-05-26 16:04:02 +00:00
ld scale = sqrt ( dh [ 1 ] * dh [ 2 ] ) * cgi . scalefactor * hcrossf7 ;
2019-05-11 18:15:09 +00:00
if ( scale < = ( WDIM = = 2 ? vid . smart_range_detail : vid . smart_range_detail_3 ) ) return false ;
2019-03-21 18:13:45 +00:00
}
else {
2019-05-26 16:04:02 +00:00
ld scale = sqrt ( dh [ 0 ] * dh [ 1 ] ) * cgi . scalefactor * hcrossf7 ;
2019-03-21 18:13:45 +00:00
if ( scale < = vid . smart_range_detail ) return false ;
}
2022-12-08 18:38:06 +00:00
2023-02-04 18:44:06 +00:00
if ( ! frustum_culling ) return true ;
2019-03-21 18:13:45 +00:00
2018-11-01 17:59:25 +00:00
return
2019-03-21 18:13:45 +00:00
x - 2 * dx < current_display - > xtop + current_display - > xsize & &
x + 2 * dx > current_display - > xtop & &
y - 2 * dy < current_display - > ytop + current_display - > ysize & &
y + 2 * dy > current_display - > ytop ;
2018-11-01 17:59:25 +00:00
}
2019-02-17 17:28:20 +00:00
# if CAP_GP
2018-04-09 15:40:12 +00:00
namespace gp {
2018-04-03 21:39:18 +00:00
/*
void drawrec ( cell * c , const transmatrix & V ) {
if ( dodrawcell ( c ) )
drawcell ( c , V , 0 , false ) ;
for ( int i = 0 ; i < c - > type ; i + + ) {
2018-08-17 22:46:45 +00:00
cell * c2 = c - > move ( i ) ;
2018-04-03 21:39:18 +00:00
if ( ! c2 ) continue ;
2018-08-17 22:46:45 +00:00
if ( c2 - > move ( 0 ) ! = c ) continue ;
2018-04-03 21:39:18 +00:00
if ( c2 = = c2 - > master - > c7 ) continue ;
2019-05-26 16:04:02 +00:00
transmatrix V1 = V * ddspin ( c , i ) * xpush ( cgi . crossf ) * iddspin ( c2 , 0 ) * spin ( M_PI ) ;
2018-04-03 21:39:18 +00:00
drawrec ( c2 , V1 ) ;
}
} */
2018-05-04 00:47:14 +00:00
2021-07-29 10:06:52 +00:00
bool drawrec ( cell * c , const shiftmatrix & V , gp : : loc at , int dir , int maindir , local_info li ) {
2018-11-19 17:58:09 +00:00
bool res = false ;
2021-07-29 10:06:52 +00:00
shiftmatrix V1 = V * cgi . gpdata - > Tf [ li . last_dir ] [ at . first & GOLDBERG_MASK ] [ at . second & GOLDBERG_MASK ] [ fixg6 ( dir ) ] ;
2018-11-23 22:45:16 +00:00
if ( do_draw ( c , V1 ) ) {
2018-05-07 18:11:04 +00:00
/* auto li = get_local_info(c);
2019-08-09 12:12:33 +00:00
if ( ( dir - li . total_dir ) % S6 ) printf ( " totaldir %d/%d \n " , dir , li . total_dir ) ;
2018-04-05 22:40:53 +00:00
if ( at ! = li . relative ) printf ( " at %s/%s \n " , disp ( at ) , disp ( li . relative ) ) ;
if ( maindir ! = li . last_dir ) printf ( " ld %d/%d \n " , maindir , li . last_dir ) ; */
2021-07-29 10:06:52 +00:00
li . relative = at ;
li . total_dir = fixg6 ( dir ) ;
current_li = li ;
li_for = c ;
2019-10-25 10:44:41 +00:00
drawcell ( c , V1 ) ;
2018-11-19 17:58:09 +00:00
res = true ;
2018-04-03 21:39:18 +00:00
}
for ( int i = 0 ; i < c - > type ; i + + ) {
2018-08-17 22:46:45 +00:00
cell * c2 = c - > move ( i ) ;
2018-04-03 21:39:18 +00:00
if ( ! c2 ) continue ;
2018-08-17 22:46:45 +00:00
if ( c2 - > move ( 0 ) ! = c ) continue ;
2018-04-03 21:39:18 +00:00
if ( c2 = = c2 - > master - > c7 ) continue ;
2021-07-29 10:06:52 +00:00
res | = drawrec ( c2 , V , at + eudir ( dir + i ) , dir + i + SG3 , maindir , li ) ;
2018-04-03 21:39:18 +00:00
}
2018-11-19 17:58:09 +00:00
return res ;
2018-04-03 21:39:18 +00:00
}
2020-07-27 16:49:04 +00:00
bool drawrec ( cell * c , const shiftmatrix & V ) {
2021-07-29 10:06:52 +00:00
local_info li ;
li . relative = loc ( 0 , 0 ) ;
li . total_dir = 0 ;
li . last_dir = - 1 ;
li . first_dir = - 1 ;
li_for = c ;
current_li = li ;
2018-11-19 17:58:09 +00:00
bool res = false ;
2018-11-10 13:26:49 +00:00
if ( do_draw ( c , V ) )
2019-10-25 10:44:41 +00:00
drawcell ( c , V ) , res = true ;
2018-04-03 21:39:18 +00:00
for ( int i = 0 ; i < c - > type ; i + + ) {
2018-08-17 22:46:45 +00:00
cell * c2 = c - > move ( i ) ;
2018-04-03 21:39:18 +00:00
if ( ! c2 ) continue ;
2018-08-17 22:46:45 +00:00
if ( c2 - > move ( 0 ) ! = c ) continue ;
2018-04-03 21:39:18 +00:00
if ( c2 = = c2 - > master - > c7 ) continue ;
2021-07-29 10:06:52 +00:00
li . last_dir = i ;
res | = drawrec ( c2 , V , gp : : loc ( 1 , 0 ) , SG3 , i , li ) ;
2018-04-03 23:19:21 +00:00
}
2018-11-19 17:58:09 +00:00
return res ;
2018-04-03 21:39:18 +00:00
}
}
2019-02-17 17:28:20 +00:00
# endif
2018-04-11 11:16:40 +00:00
2020-07-27 16:49:04 +00:00
vector < tuple < heptspin , hstate , shiftmatrix > > drawn_cells ;
2019-04-03 18:30:35 +00:00
2020-07-27 16:49:04 +00:00
EX bool drawcell_subs ( cell * c , const shiftmatrix & V ) {
2019-11-25 19:05:52 +00:00
# if CAP_GP
if ( GOLDBERG ) {
return gp : : drawrec ( c , V ) ;
}
# endif
bool draw = false ;
# if CAP_IRR
if ( IRREGULAR ) {
auto & hi = irr : : periodmap [ c - > master ] ;
auto & vc = irr : : cells_of_heptagon [ hi . base . at ] ;
for ( int i = 0 ; i < isize ( vc ) ; i + + ) {
cell * c = hi . subcells [ i ] ;
2020-07-27 16:49:04 +00:00
shiftmatrix V1 = V * irr : : cells [ vc [ i ] ] . pusher ;
2019-11-25 19:05:52 +00:00
if ( do_draw ( c , V1 ) )
draw = true ,
drawcell ( hi . subcells [ i ] , V * irr : : cells [ vc [ i ] ] . pusher ) ;
}
return draw ;
}
# endif
2019-11-27 00:01:20 +00:00
if ( do_draw ( c , V ) ) {
draw = true ;
2019-11-25 19:05:52 +00:00
drawcell ( c , V ) ;
2019-11-27 00:01:20 +00:00
}
2019-11-25 19:05:52 +00:00
if ( BITRUNCATED ) forCellIdEx ( c1 , d , c ) {
if ( c - > c . spin ( d ) = = 0 ) {
2020-07-27 16:49:04 +00:00
shiftmatrix V2 = V * currentmap - > adj ( c , d ) ;
2019-11-25 19:05:52 +00:00
if ( do_draw ( c1 , V2 ) )
draw = true ,
drawcell ( c1 , V2 ) ;
}
}
return draw ;
}
2020-07-27 17:36:19 +00:00
void hrmap : : draw_all ( ) {
if ( sphere & & pmodel = = mdSpiral ) {
2019-08-09 22:58:50 +00:00
if ( models : : ring_not_spiral ) {
2020-04-16 22:53:58 +00:00
int qty = ceil ( 1. / pconf . sphere_spiral_multiplier ) ;
2019-04-03 18:30:35 +00:00
if ( qty > 100 ) qty = 100 ;
2020-07-27 17:36:19 +00:00
for ( int i = - qty ; i < qty ; i + + )
2022-11-12 21:38:45 +00:00
draw_at ( centerover , cview ( TAU * i ) ) ;
2019-04-03 18:30:35 +00:00
}
else {
2020-07-27 17:36:19 +00:00
draw_at ( centerover , cview ( ) ) ;
2019-04-03 18:30:35 +00:00
if ( vid . use_smart_range ) for ( int i = 1 ; ; i + + ) {
int drawn = cells_drawn ;
2022-11-12 21:38:45 +00:00
draw_at ( centerover , cview ( TAU * i ) ) ;
draw_at ( centerover , cview ( - TAU * i ) ) ;
2019-04-03 18:30:35 +00:00
if ( drawn = = cells_drawn ) break ;
}
}
}
2020-07-27 17:36:19 +00:00
else
draw_at ( centerover , cview ( ) ) ;
}
void hrmap : : draw_at ( cell * at , const shiftmatrix & where ) {
dq : : clear_all ( ) ;
auto & enq = confusingGeometry ( ) ? dq : : enqueue_by_matrix_c : dq : : enqueue_c ;
enq ( at , where ) ;
while ( ! dq : : drawqueue_c . empty ( ) ) {
auto & p = dq : : drawqueue_c . front ( ) ;
cell * c = p . first ;
shiftmatrix V = p . second ;
dq : : drawqueue_c . pop ( ) ;
if ( ! do_draw ( c , V ) ) continue ;
drawcell ( c , V ) ;
if ( in_wallopt ( ) & & isWall3 ( c ) & & isize ( dq : : drawqueue ) > 1000 ) continue ;
2020-07-30 00:29:59 +00:00
# if MAXMDIM >= 4
2020-07-27 17:36:19 +00:00
if ( reg3 : : ultra_mirror_in ( ) )
for ( auto & T : cgi . ultra_mirrors )
enq ( c , optimized_shift ( V * T ) ) ;
2020-07-30 00:29:59 +00:00
# endif
2020-07-27 17:36:19 +00:00
for ( int i = 0 ; i < c - > type ; i + + ) {
// note: need do cmove before c.spin
cell * c1 = c - > cmove ( i ) ;
2021-06-16 10:45:39 +00:00
if ( c1 = = & out_of_bounds ) continue ;
2020-07-27 17:36:19 +00:00
enq ( c1 , optimized_shift ( V * adj ( c , i ) ) ) ;
}
}
}
void hrmap_standard : : draw_at ( cell * at , const shiftmatrix & where ) {
2020-11-01 10:32:12 +00:00
if ( S3 > 4 ) {
hrmap : : draw_at ( at , where ) ;
return ;
}
2018-11-23 22:45:16 +00:00
drawn_cells . clear ( ) ;
2020-07-27 17:36:19 +00:00
drawn_cells . emplace_back ( at - > master , hsOrigin , where * master_relative ( at , true ) ) ;
2018-11-30 16:17:06 +00:00
for ( int i = 0 ; i < isize ( drawn_cells ) ; i + + ) {
// prevent reallocation due to insertion
if ( drawn_cells . capacity ( ) < drawn_cells . size ( ) + 16 )
2018-12-13 13:54:26 +00:00
drawn_cells . reserve ( max < size_t > ( 2 * drawn_cells . size ( ) , 128 ) ) ;
2018-11-30 16:17:06 +00:00
2018-11-23 22:45:16 +00:00
const auto & dc = drawn_cells [ i ] ;
auto & hs = get < 0 > ( dc ) ;
auto & s = get < 1 > ( dc ) ;
auto & V = get < 2 > ( dc ) ;
2018-11-10 13:26:49 +00:00
2018-11-23 22:45:16 +00:00
cell * c = hs . at - > c7 ;
2020-07-27 16:49:04 +00:00
const shiftmatrix & V1 = hs . mirrored ? V * Mirror : V ;
2018-11-23 22:45:16 +00:00
2019-11-25 19:05:52 +00:00
bool draw = drawcell_subs ( c , actualV ( hs , V1 ) ) ;
2019-04-03 18:30:35 +00:00
if ( sphere ) draw = true ;
2020-01-18 23:13:54 +00:00
2020-02-26 00:41:27 +00:00
if ( draw ) for ( int d = 0 ; d < c - > master - > type ; d + + ) {
2018-11-23 22:45:16 +00:00
hstate s2 = transition ( s , d ) ;
if ( s2 = = hsError ) continue ;
heptspin hs2 = hs + d + wstep ;
2020-07-27 16:49:04 +00:00
shiftmatrix Vd ;
2020-01-18 23:13:54 +00:00
if ( inforder : : mixed ( ) ) {
int d1 = gmod ( hs . spin + d , c - > type ) ;
2022-11-12 21:38:45 +00:00
Vd = V * spin ( - TAU * d / c - > type ) * xpush ( spacedist ( c , d1 ) ) * spin180 ( ) ;
2020-01-18 23:13:54 +00:00
}
else
Vd = V * cgi . heptmove [ d ] ;
2020-07-27 16:49:04 +00:00
optimize_shift ( Vd ) ;
drawn_cells . emplace_back ( hs2 , s2 , Vd ) ;
2018-11-23 22:45:16 +00:00
}
}
2017-07-10 18:47:38 +00:00
}
2024-06-17 21:10:09 +00:00
EX bool has_fixed_yz ( ) {
2024-10-11 07:41:37 +00:00
if ( walking : : on ) return false ;
2024-06-27 20:26:39 +00:00
return ( embedded_plane | | mhybrid | | nil | | ( euclid & & WDIM = = 3 ) | | sol | | nih | | ( cgflags & qSTRETCHABLE ) | | ( hyperbolic & & bt : : in ( ) ) ) ;
2024-06-17 21:10:09 +00:00
}
2020-04-19 19:56:26 +00:00
EX bool keep_vertical ( ) {
2024-06-17 21:10:09 +00:00
if ( vid . fixed_yz & & has_fixed_yz ( ) ) return ! CAP_ORIENTATION ;
2020-04-19 19:56:26 +00:00
if ( downseek . qty ) return true ;
return false ;
}
EX hyperpoint vertical_vector ( ) {
auto & ds = downseek ;
2023-02-03 15:11:13 +00:00
if ( embedded_plane & & vid . fixed_yz ) {
2023-01-26 23:27:10 +00:00
transmatrix Rot = View * cgi . emb - > map_relative_push ( inverse ( View ) * C0 ) ;
2023-02-03 15:11:13 +00:00
if ( gproduct ) Rot = NLP * Rot ;
2023-01-07 17:56:12 +00:00
return Rot * lztangent ( vid . wall_height ) ;
}
2024-06-17 21:10:09 +00:00
if ( mproduct & & vid . fixed_yz ) {
2023-02-03 15:11:13 +00:00
return get_view_orientation ( ) * lztangent ( 1 ) ;
2022-12-15 17:15:26 +00:00
}
2024-08-21 17:20:47 +00:00
if ( ( ( cgflags & qSTRETCHABLE ) | | mtwisted ) & & vid . fixed_yz ) {
2024-06-17 21:10:09 +00:00
return stretch : : itranslate ( View * C0 ) * View * lztangent ( 1 ) ;
}
2024-06-29 08:58:29 +00:00
if ( ( nil | | ( euclid & & GDIM = = 3 ) | | sol | | nih ) & & vid . fixed_yz ) {
2024-06-17 21:10:09 +00:00
return View * lztangent ( 1 ) ;
}
if ( hyperbolic & & bt : : in ( ) & & vid . fixed_yz ) {
hyperpoint h = inverse ( View ) * C0 ;
return View * parabolic13_at ( deparabolic13 ( h ) ) * xtangent ( 1 ) ;
}
2023-02-03 15:11:13 +00:00
if ( ds . qty & & gproduct )
2020-04-19 19:56:26 +00:00
return get_view_orientation ( ) * product : : inverse_exp ( ds . point ) ;
2023-02-03 15:11:13 +00:00
if ( ds . qty )
2020-04-19 19:56:26 +00:00
return ds . point ;
return C0 ;
}
2019-08-09 19:00:52 +00:00
EX void spinEdge ( ld aspd ) {
2020-11-19 17:20:06 +00:00
# if CAP_VR
2020-12-30 13:20:30 +00:00
if ( vrhr : : active ( ) & & keep_vertical ( ) & & ! vrhr : : first ) {
2020-11-19 17:20:06 +00:00
transmatrix T = vrhr : : hmd_ref_at ;
T = vrhr : : sm * inverse ( T ) ;
vrhr : : be_33 ( T ) ;
transmatrix V = T * get_view_orientation ( ) ;
2021-02-06 12:04:18 +00:00
2020-11-19 17:20:06 +00:00
hyperpoint h = inverse ( V ) * C0 ;
2022-12-08 18:38:06 +00:00
if ( ! gproduct ) {
2023-02-21 17:48:19 +00:00
if ( embedded_plane )
V = V * cgi . emb - > map_relative_push ( h ) ;
else
V = V * rgpushxto0 ( h ) ;
2021-02-04 20:42:25 +00:00
}
2020-11-19 17:20:06 +00:00
2023-02-21 17:48:19 +00:00
V = cspin90 ( 2 , 1 ) * V ;
if ( vid . wall_height < 0 ) V = cspin180 ( 1 , 2 ) * V ;
V = V * cgi . emb - > logical_scaled_to_intermediate ;
2020-11-19 17:20:06 +00:00
if ( 1 ) {
dynamicval < eGeometry > g ( geometry , gSphere ) ;
2023-02-21 17:48:19 +00:00
bool em = embedded_plane ;
if ( em ) geom3 : : light_flip ( true ) ;
2020-11-19 17:20:06 +00:00
V = gpushxto0 ( V * C0 ) * V ;
2021-02-06 12:03:40 +00:00
fixmatrix ( V ) ;
2023-02-21 17:48:19 +00:00
if ( em ) geom3 : : light_flip ( false ) ;
2020-11-19 17:20:06 +00:00
}
2020-11-22 18:59:34 +00:00
vrhr : : be_33 ( V ) ;
2023-02-21 17:48:19 +00:00
if ( vid . wall_height < 0 ) V = cspin180 ( 1 , 2 ) * V ;
V = cspin90 ( 1 , 2 ) * V ;
V = V * cgi . emb - > intermediate_to_logical_scaled ;
if ( ! gproduct ) {
if ( embedded_plane )
V = V * inverse ( cgi . emb - > map_relative_push ( h ) ) ;
else
V = V * gpushxto0 ( h ) ;
}
2021-02-04 20:42:25 +00:00
V = inverse ( T ) * V ;
2023-02-21 17:48:19 +00:00
rotate_view ( V * inverse ( get_view_orientation ( ) ) ) ;
2020-11-19 17:20:06 +00:00
return ;
}
# endif
2019-05-13 13:09:24 +00:00
ld downspin = 0 ;
2019-12-27 10:14:25 +00:00
auto & ds = downseek ;
2020-01-30 16:45:51 +00:00
if ( dual : : state = = 2 & & ( dual : : one_euclidean ? ! euclid : dual : : currently_loaded ! = dual : : main_side ) ) {
2019-06-24 21:00:12 +00:00
transmatrix our = dual : : get_orientation ( ) ;
2019-05-28 23:09:38 +00:00
transmatrix their = dual : : player_orientation [ dual : : main_side ] ;
fixmatrix ( our ) ;
fixmatrix ( their ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 2 ) {
2020-09-16 03:57:05 +00:00
transmatrix T = their * iso_inverse ( our ) ;
2019-05-28 23:09:38 +00:00
hyperpoint H = T * xpush0 ( 1 ) ;
downspin = - atan2 ( H [ 1 ] , H [ 0 ] ) ;
}
2020-09-16 03:57:05 +00:00
else rotate_view ( their * iso_inverse ( our ) ) ;
2019-05-28 23:09:38 +00:00
}
else if ( playerfound & & vid . fixed_facing ) {
2020-07-27 16:49:04 +00:00
hyperpoint H = gpushxto0 ( unshift ( playerV ) * C0 ) * unshift ( playerV ) * xpush0 ( 5 ) ;
2019-01-17 13:09:00 +00:00
downspin = atan2 ( H [ 1 ] , H [ 0 ] ) ;
downspin + = vid . fixed_facing_dir * degree ;
if ( flipplayer ) downspin + = M_PI ;
2020-04-19 19:56:26 +00:00
cyclefix ( downspin , 0 ) ;
2019-01-17 13:09:00 +00:00
aspd = ( 1 + 2 * abs ( downspin ) ) * aspd ;
}
2020-04-19 19:56:26 +00:00
else if ( keep_vertical ( ) ) {
hyperpoint h = vertical_vector ( ) ;
if ( ds . qty & & GDIM = = 2 ) {
2023-08-14 15:02:34 +00:00
h = rot_inverse ( models : : rotation . get ( ) ) * h ;
2019-05-13 13:09:24 +00:00
}
2023-08-14 15:02:34 +00:00
downspin = - atan2 ( h [ 0 ] , h [ 1 ] ) ;
2020-04-19 20:00:29 +00:00
if ( ds . qty ) {
2020-04-19 19:56:26 +00:00
cyclefix ( downspin , 0 ) ;
downspin = downspin * min ( ds . speed , ( double ) 1 ) ;
2019-05-13 13:09:24 +00:00
}
2020-04-19 19:56:26 +00:00
else aspd = 999999 ;
2019-05-13 13:09:24 +00:00
}
2017-07-10 18:47:38 +00:00
if ( downspin > aspd ) downspin = aspd ;
if ( downspin < - aspd ) downspin = - aspd ;
2022-12-15 17:15:26 +00:00
rotate_view ( cspin ( 0 , 1 , downspin ) ) ;
2017-07-10 18:47:38 +00:00
}
2023-05-15 00:12:20 +00:00
EX void spinEdge_full ( ) { spinEdge ( 999999 ) ; }
2021-05-30 11:00:42 +00:00
/** \brief convert a shiftmatrix to the coordinate system of View
* usually used to set which_copy
*/
EX transmatrix back_to_view ( const shiftmatrix & V ) {
// ortho_inverse does not work in 2.5D, iso_inverse does not work in Nil.
// just use inverse
return inverse ( actual_view_transform ) * unshift ( V ) ;
}
EX void fix_whichcopy ( cell * c ) {
if ( ! gmatrix . count ( cwt . at ) ) return ;
current_display - > which_copy = back_to_view ( gmatrix [ c ] ) ;
}
void fix_whichcopy_if_near ( ) {
if ( ! gmatrix . count ( cwt . at ) ) return ;
transmatrix T = back_to_view ( gmatrix [ cwt . at ] ) ;
if ( ! eqmatrix ( T , current_display - > which_copy ) ) return ;
current_display - > which_copy = T ;
}
2022-12-17 16:37:26 +00:00
EX void adjust_eye ( transmatrix & T , cell * c , ld sign ) {
2022-12-08 18:38:06 +00:00
if ( ! embedded_plane ) return ;
geom3 : : do_auto_eye ( ) ;
int sl = snakelevel ( c ) ;
if ( isWorm ( c - > monst ) & & sl < 3 ) sl + + ;
2023-01-26 23:27:10 +00:00
ld i = cgi . emb - > center_z ( ) ;
2022-12-08 18:38:06 +00:00
if ( sl | | vid . eye | | i )
2022-12-17 16:37:26 +00:00
T = T * lzpush ( sign * ( cgi . SLEV [ sl ] - cgi . FLOOR - vid . eye + i ) ) ;
}
2023-01-07 21:51:46 +00:00
EX bool shmup_inverted ( ) {
if ( ! embedded_plane ) return false ;
2023-02-04 18:40:49 +00:00
return vid . wall_height < 0 ;
2023-01-07 21:51:46 +00:00
}
2024-06-29 08:48:57 +00:00
/** create the list of matrices that are affected when we shift/rotate the view. Usually this is not only the View matrix, but also auxiliary ones such as which_copy.
* flag & 1 : spins , so return only NLP if get_view_orientation ( ) is NLP
* flag & 2 : if rugged , only View
*/
EX vector < transmatrix * > move_affected_matrices ( int flag ) {
if ( flag & 1 ) {
if ( & get_view_orientation ( ) = = & NLP ) return { & NLP } ;
}
if ( ( flag & 2 ) & & rug : : rugged ) return { & View } ;
vector < transmatrix * > res ;
res . push_back ( & View ) ;
res . push_back ( & current_display - > which_copy ) ;
res . push_back ( & cwtV . T ) ;
if ( mapeditor : : dt_in ( ) ) res . push_back ( & mapeditor : : cfree_old . T ) ;
return res ;
}
2021-05-30 11:00:42 +00:00
EX void centerpc ( ld aspd ) {
2023-01-05 23:24:45 +00:00
2019-03-09 15:20:06 +00:00
if ( subscreens : : split ( [ = ] ( ) { centerpc ( aspd ) ; } ) ) return ;
2019-05-28 23:09:38 +00:00
if ( dual : : split ( [ = ] ( ) { centerpc ( aspd ) ; } ) ) return ;
2019-03-09 15:20:06 +00:00
2020-07-03 12:42:33 +00:00
# if CAP_CRYSTAL && CAP_RUG
2019-08-22 10:14:39 +00:00
if ( cryst )
2018-12-04 17:10:36 +00:00
crystal : : centerrug ( aspd ) ;
2019-02-17 17:28:20 +00:00
# endif
2019-05-11 21:55:02 +00:00
# if CAP_RACING
if ( racing : : on & & racing : : set_view ( ) ) return ;
# endif
2019-02-25 13:51:51 +00:00
2019-05-10 01:16:40 +00:00
# if MAXMDIM >= 4
2019-08-15 13:05:43 +00:00
if ( shmup : : on & & vid . sspeed > - 5 & & GDIM = = 3 ) {
2019-03-09 15:20:06 +00:00
int id = subscreens : : in ? subscreens : : current_player : 0 ;
2019-08-24 18:29:58 +00:00
auto & pc = shmup : : pc [ id ] ;
2019-11-13 23:26:50 +00:00
centerover = pc - > base ;
2019-08-24 18:29:58 +00:00
transmatrix T = pc - > at ;
2022-12-17 18:03:36 +00:00
2023-02-04 18:40:49 +00:00
if ( embedded_plane ) {
transmatrix rot = inverse ( cgi . emb - > intermediate_to_actual_translation ( cgi . emb - > actual_to_intermediate ( T * tile_center ( ) ) ) ) * T ;
2022-12-17 18:03:36 +00:00
T = T * inverse ( rot ) ;
adjust_eye ( T , pc - > base , + 1 ) ;
T = T * rot ;
}
else {
adjust_eye ( T , pc - > base , + 1 ) ;
}
2023-01-07 21:51:46 +00:00
2022-12-17 16:37:26 +00:00
View = iview_inverse ( T ) ;
2022-12-08 18:38:06 +00:00
if ( gproduct ) NLP = ortho_inverse ( pc - > ori ) ;
2022-12-17 16:37:26 +00:00
if ( WDIM = = 2 ) {
2023-02-04 18:40:49 +00:00
// already taken into account in gproduct
rotate_view ( cgi . emb - > intermediate_to_logical_scaled ) ;
// right => upwards
rotate_view ( cspin90 ( 0 , 1 ) ) ;
// apply playerturny
2023-01-07 21:51:46 +00:00
if ( shmup_inverted ( ) ) rotate_view ( cspin180 ( 2 , 1 ) ) ;
2023-02-04 18:40:49 +00:00
rotate_view ( cspin ( 2 , 1 , - 90. _deg - shmup : : playerturny [ id ] ) ) ;
2022-12-17 16:37:26 +00:00
}
2018-11-17 18:30:50 +00:00
return ;
}
# endif
2018-12-04 17:10:36 +00:00
2018-07-23 03:14:19 +00:00
if ( ors : : mode = = 2 & & vid . sspeed < 5 ) return ;
2017-07-10 18:47:38 +00:00
if ( vid . sspeed > = 4.99 ) aspd = 1000 ;
2019-05-12 23:57:40 +00:00
DEBBI ( DF_GRAPH , ( " center pc " ) ) ;
2019-11-23 18:05:24 +00:00
2024-06-29 08:48:57 +00:00
auto mam = move_affected_matrices ( 0 ) ;
for ( auto pV : mam ) ors : : unrotate ( * pV ) ;
2019-11-23 18:05:24 +00:00
/* what should we center? */
transmatrix T ;
if ( multi : : players > 1 )
2020-07-27 16:49:04 +00:00
T = unshift ( cwtV ) ; /* do not even try */
2019-11-23 18:05:24 +00:00
else {
2024-06-29 08:48:57 +00:00
T = current_display - > which_copy ;
2019-11-23 18:05:24 +00:00
if ( shmup : : on )
T = T * shmup : : pc [ 0 ] - > at ;
}
2018-07-23 03:14:19 +00:00
2019-11-23 18:05:24 +00:00
if ( invalid_matrix ( T ) ) return ;
2022-12-17 16:37:26 +00:00
adjust_eye ( T , cwt . at , + 1 ) ;
2022-12-08 18:38:06 +00:00
2021-05-30 11:00:42 +00:00
hyperpoint H = tC0 ( T ) ;
2022-12-08 18:38:06 +00:00
ld R = ( zero_d ( GDIM , H ) & & ! gproduct ) ? 0 : hdist0 ( H ) ;
2022-12-17 16:37:26 +00:00
2017-07-10 18:47:38 +00:00
if ( R < 1e-9 ) {
2018-03-25 13:07:11 +00:00
// either already centered or direction unknown
2017-07-10 18:47:38 +00:00
/* if(playerfoundL && playerfoundR) {
} */
2022-12-08 18:38:06 +00:00
2017-07-10 18:47:38 +00:00
spinEdge ( aspd ) ;
2021-05-30 11:00:42 +00:00
fix_whichcopy ( cwt . at ) ;
2024-06-29 08:48:57 +00:00
for ( auto pV : mam ) fixmatrix ( * pV ) ;
2017-07-10 18:47:38 +00:00
}
else {
2019-11-23 18:00:18 +00:00
aspd * = euclid ? ( 2 + 3 * R * R ) : ( 1 + R + ( shmup : : on ? 1 : 0 ) ) ;
2020-03-08 00:24:34 +00:00
2021-05-30 11:00:42 +00:00
if ( R < aspd ) fix_whichcopy_if_near ( ) ;
2019-08-17 21:28:41 +00:00
2019-08-24 16:14:38 +00:00
if ( R < aspd )
2022-12-17 10:47:10 +00:00
shift_view_to ( shiftless ( H ) , shift_method ( smaAutocenter ) ) ;
2017-07-10 18:47:38 +00:00
else
2022-12-17 10:47:10 +00:00
shift_view_towards ( shiftless ( H ) , aspd , shift_method ( smaAutocenter ) ) ;
2017-07-10 18:47:38 +00:00
2024-06-29 08:48:57 +00:00
for ( auto pV : mam ) fixmatrix ( * pV ) ;
2017-07-10 18:47:38 +00:00
spinEdge ( aspd ) ;
}
2018-07-23 03:14:19 +00:00
2022-03-27 12:32:29 +00:00
if ( set_multi & & multi : : two_focus ) {
2023-08-16 08:25:57 +00:00
pconf . mori ( ) = spin ( - atan2 ( multi_point ) ) ;
2022-03-27 12:32:29 +00:00
auto & d = pconf . twopoint_param ;
d = hdist0 ( multi_point ) ;
if ( among ( pmodel , mdJoukowsky , mdJoukowskyInverted ) ) {
2023-08-08 14:27:52 +00:00
pconf . mori ( ) = pconf . mori ( ) * spin90 ( ) ;
2022-03-27 12:32:29 +00:00
pconf . model_transition = sinh ( d ) / ( 1 + cosh ( d ) ) ;
pconf . dualfocus_autoscale = true ;
}
}
2024-06-29 08:48:57 +00:00
for ( auto pV : mam ) ors : : rerotate ( * pV ) ;
2017-07-10 18:47:38 +00:00
}
2021-03-21 09:42:36 +00:00
EX transmatrix oView ;
2021-04-06 23:11:25 +00:00
EX purehookset hooks_preoptimize , hooks_postoptimize ;
2024-10-19 23:52:02 +00:00
EX bool dont_optimize ;
2019-08-09 19:00:52 +00:00
EX void optimizeview ( ) {
2018-11-17 18:30:50 +00:00
2019-03-09 15:20:06 +00:00
if ( subscreens : : split ( optimizeview ) ) return ;
2019-05-28 23:09:38 +00:00
if ( dual : : split ( optimizeview ) ) return ;
2024-10-19 23:52:02 +00:00
if ( dont_optimize ) return ;
2017-07-10 18:47:38 +00:00
2020-08-22 22:10:59 +00:00
cell * c = centerover ;
2020-09-16 03:57:05 +00:00
transmatrix iView = view_inverse ( View ) ;
2021-04-06 23:11:25 +00:00
callhooks ( hooks_preoptimize ) ;
2019-11-14 18:33:55 +00:00
virtualRebase ( centerover , iView ) ;
2020-08-22 22:16:14 +00:00
if ( c ! = centerover & & ( sphere | | sl2 ) ) {
2020-08-22 22:10:59 +00:00
transmatrix T = currentmap - > relative_matrix ( centerover , c , C0 ) ;
T = stretch : : itranslate ( tC0 ( T ) ) * T ;
stretch : : mstretch_matrix = T * stretch : : mstretch_matrix ;
}
2020-09-16 03:57:05 +00:00
View = iview_inverse ( iView ) ;
2019-11-16 00:41:44 +00:00
fixmatrix ( View ) ;
2021-04-06 23:11:25 +00:00
callhooks ( hooks_postoptimize ) ;
2023-11-30 11:34:41 +00:00
# if CAP_PORTALS
intra : : apply_scale ( ) ;
# endif
2021-03-21 09:42:36 +00:00
2022-02-26 09:40:39 +00:00
walking : : handle ( ) ;
2021-03-21 09:42:36 +00:00
if ( is_boundary ( centerover ) )
centerover = c , View = oView ;
else
oView = View ;
2019-11-16 00:41:44 +00:00
2019-02-17 17:43:39 +00:00
# if CAP_ANIMATIONS
2019-11-13 23:26:50 +00:00
if ( centerover & & inmirror ( centerover ) ) {
2018-09-23 21:55:03 +00:00
anims : : reflect_view ( ) ;
}
2019-02-17 17:43:39 +00:00
# endif
2017-07-10 18:47:38 +00:00
}
void addball ( ld a , ld b , ld c ) {
hyperpoint h ;
ballmodel ( h , a , b , c ) ;
2018-11-17 18:24:02 +00:00
for ( int i = 0 ; i < 3 ; i + + ) h [ i ] * = current_display - > radius ;
2017-07-10 18:47:38 +00:00
curvepoint ( h ) ;
}
void ballgeometry ( ) {
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , PPR : : CIRCLE ) ;
2017-07-10 18:47:38 +00:00
for ( int i = 0 ; i < 60 ; i + + )
2022-11-12 21:38:45 +00:00
addball ( TAU * i / 60 , 10 , 0 ) ;
2017-07-10 18:47:38 +00:00
for ( double d = 10 ; d > = - 10 ; d - = .2 )
addball ( 0 , d , 0 ) ;
for ( double d = - 10 ; d < = 10 ; d + = .2 )
2019-05-29 14:27:24 +00:00
addball ( 0 , d , vid . depth ) ;
addball ( 0 , 0 , - vid . camera ) ;
addball ( 0 , 0 , vid . depth ) ;
addball ( 0 , 0 , - vid . camera ) ;
2017-07-10 18:47:38 +00:00
addball ( 0 , - 10 , 0 ) ;
2019-05-29 14:27:24 +00:00
addball ( 0 , 0 , - vid . camera ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , darkena ( 0xFF , 0 , 0x80 ) , 0 , PPR : : CIRCLE ) ;
2018-08-28 12:27:23 +00:00
queuereset ( pmodel , PPR : : CIRCLE ) ;
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX void resetview ( ) {
2019-05-12 23:57:40 +00:00
DEBBI ( DF_GRAPH , ( " reset view " ) ) ;
2017-07-10 18:47:38 +00:00
// EUCLIDEAN
2023-02-05 09:31:58 +00:00
decide_lpu ( ) ;
2019-11-14 18:24:42 +00:00
NLP = Id ;
2020-08-22 22:10:59 +00:00
stretch : : mstretch_matrix = Id ;
2023-01-05 23:09:12 +00:00
auto & vo = get_view_orientation ( ) ;
2019-11-13 23:43:36 +00:00
if ( cwt . at ) {
centerover = cwt . at ;
2019-11-14 18:34:09 +00:00
View = iddspin ( cwt . at , cwt . spin ) ;
2023-01-05 23:09:12 +00:00
if ( ! flipplayer ) vo = spin180 ( ) * vo ;
if ( cwt . mirrored ) vo = lmirror ( ) * vo ;
2020-04-01 10:01:55 +00:00
if ( centering ) {
hyperpoint vl = View * get_corner_position ( cwt . at , cwt . spin ) ;
hyperpoint vr = View * get_corner_position ( cwt . at , ( cwt . spin + 1 ) % cwt . at - > type ) ;
hyperpoint vm = ( centering = = eCentering : : edge ) ? mid ( vl , vr ) : vl ;
transmatrix rm = gpushxto0 ( vm ) ;
View = spintox ( rm * vr ) * rm * View ;
}
2019-11-13 23:43:36 +00:00
}
else if ( currentmap ) {
centerover = currentmap - > gamestart ( ) ;
2022-12-17 16:37:26 +00:00
View = Id ;
2019-11-13 23:43:36 +00:00
}
2022-12-08 18:38:06 +00:00
2022-12-17 18:03:36 +00:00
adjust_eye ( View , cwt . at , - 1 ) ;
2023-01-05 23:09:12 +00:00
if ( WDIM = = 2 ) vo = spin ( M_PI + vid . fixed_facing_dir * degree ) * vo ;
if ( WDIM = = 3 ) vo = cspin90 ( 0 , 2 ) * vo ;
2023-01-26 23:27:10 +00:00
vo = cgi . emb - > intermediate_to_logical_scaled * vo ;
2023-01-05 23:09:12 +00:00
if ( embedded_plane ) vo = cspin90 ( 1 , 2 ) * vo ;
if ( embedded_plane & & vid . wall_height < 0 ) vo = cspin180 ( 0 , 1 ) * vo ;
2022-12-17 16:37:26 +00:00
2020-07-27 16:49:04 +00:00
cwtV = shiftless ( View ) ;
2019-11-23 19:16:53 +00:00
current_display - > which_copy =
2020-09-16 03:57:05 +00:00
nonisotropic ? gpushxto0 ( tC0 ( view_inverse ( View ) ) ) :
2019-11-23 19:16:53 +00:00
View ;
2017-07-10 18:47:38 +00:00
// SDL_LockSurface(s);
// SDL_UnlockSurface(s);
}
2020-07-27 16:49:04 +00:00
EX void panning ( shiftpoint hf0 , shiftpoint ht0 ) {
hyperpoint hf = hf0 . h ;
hyperpoint ht = unshift ( ht0 , hf0 . shift ) ;
2017-07-10 18:47:38 +00:00
View =
rgpushxto0 ( hf ) * rgpushxto0 ( gpushxto0 ( hf ) * ht ) * gpushxto0 ( hf ) * View ;
playermoved = false ;
}
2019-08-09 19:00:52 +00:00
EX int cells_drawn , cells_generated ;
2018-11-01 17:59:25 +00:00
2019-08-09 19:00:52 +00:00
EX void fullcenter ( ) {
2020-04-01 09:26:19 +00:00
if ( history : : saved_ends = = 0 )
history : : path_for_lineanimation . clear ( ) ;
2017-07-10 18:47:38 +00:00
if ( playerfound & & false ) centerpc ( INF ) ;
else {
bfs ( ) ;
2022-12-17 16:37:26 +00:00
resetview ( ) ;
2017-07-10 18:47:38 +00:00
drawthemap ( ) ;
2020-04-01 10:01:55 +00:00
if ( ! centering ) centerpc ( INF ) ;
2018-08-21 16:23:31 +00:00
centerover = cwt . at ;
2017-07-10 18:47:38 +00:00
}
2020-04-01 10:01:55 +00:00
playermoved = ! centering ;
2017-07-10 18:47:38 +00:00
}
transmatrix screenpos ( ld x , ld y ) {
transmatrix V = Id ;
2020-04-16 22:53:58 +00:00
V [ 0 ] [ 2 ] + = ( x - current_display - > xcenter ) / current_display - > radius * ( 1 + pconf . alpha ) ;
V [ 1 ] [ 2 ] + = ( y - current_display - > ycenter ) / current_display - > radius * ( 1 + pconf . alpha ) ;
2017-07-10 18:47:38 +00:00
return V ;
}
2019-10-21 20:34:20 +00:00
/**
In 3 D , we use the standard translation matrices to place stuff on the screen .
In 2 D , this does not work ( as HyperRogue reduces matrices to 3 x3 ) so we use the native disk projection
*/
2020-08-01 14:42:15 +00:00
EX int flat_on ;
2020-07-26 13:05:42 +00:00
eGeometry backup_geometry ;
2021-08-04 16:23:26 +00:00
eVariation backup_variation ;
2020-08-01 14:42:15 +00:00
videopar backup_vid ;
2022-12-25 11:14:36 +00:00
bool backup_lpu ;
2023-08-14 16:08:28 +00:00
transmatrix backup_cam ;
2019-08-19 07:39:19 +00:00
2020-04-17 13:54:48 +00:00
/** \brief enable the 'flat' model for drawing HUD. See hr::flat_model_enabler */
2020-07-26 13:05:42 +00:00
EX void enable_flat_model ( int val ) {
if ( flat_on < 1 & & flat_on + val > = 1 ) {
# if CAP_GL
glClear ( GL_DEPTH_BUFFER_BIT ) ;
2020-07-25 10:00:01 +00:00
# endif
2020-07-26 13:05:42 +00:00
backup_geometry = geometry ;
2021-08-04 16:23:26 +00:00
backup_variation = variation ;
2023-08-14 16:08:28 +00:00
backup_cam = pconf . cam ( ) ;
2022-12-25 11:14:36 +00:00
backup_lpu = nisot : : local_perspective_used ;
2020-08-01 14:42:15 +00:00
backup_vid = vid ;
2020-07-26 13:05:42 +00:00
geometry = gNormal ;
2021-08-04 16:23:26 +00:00
variation = eVariation : : bitruncated ;
2022-12-25 11:14:36 +00:00
nisot : : local_perspective_used = false ;
2020-07-26 13:05:42 +00:00
pmodel = mdDisk ;
pconf . alpha = 1 ;
pconf . scale = 1 ;
2023-08-14 16:08:28 +00:00
pconf . cam ( ) = Id ;
2020-07-26 13:05:42 +00:00
pconf . stretch = 1 ;
2020-08-01 14:42:15 +00:00
2020-07-26 13:05:42 +00:00
vid . always3 = false ;
2020-08-01 14:42:15 +00:00
vid . wall_height = .3 ;
vid . human_wall_ratio = .7 ;
vid . camera = 1 ;
vid . depth = 1 ;
2020-07-29 19:49:38 +00:00
geom3 : : apply_always3 ( ) ;
2020-07-26 13:05:42 +00:00
check_cgi ( ) ;
cgi . require_shapes ( ) ;
calcparam ( ) ;
2020-07-25 10:00:01 +00:00
}
2020-07-26 13:05:42 +00:00
if ( flat_on > = 1 & & flat_on + val < 1 ) {
geometry = backup_geometry ;
2021-08-04 16:23:26 +00:00
variation = backup_variation ;
2022-12-25 11:14:36 +00:00
nisot : : local_perspective_used = backup_lpu ;
2023-08-14 16:08:28 +00:00
pconf . cam ( ) = backup_cam ;
2020-08-01 14:42:15 +00:00
vid = backup_vid ;
2020-07-29 19:49:38 +00:00
geom3 : : apply_always3 ( ) ;
2020-07-25 10:00:01 +00:00
calcparam ( ) ;
2020-07-26 13:05:42 +00:00
check_cgi ( ) ;
2020-07-25 10:00:01 +00:00
}
2020-07-26 13:05:42 +00:00
flat_on + = val ;
}
# if HDR
struct flat_model_enabler {
flat_model_enabler ( ) { enable_flat_model ( + 1 ) ; }
~ flat_model_enabler ( ) { enable_flat_model ( - 1 ) ; }
2020-04-17 13:54:48 +00:00
} ;
# endif
2019-08-09 19:00:52 +00:00
EX transmatrix atscreenpos ( ld x , ld y , ld size ) {
2017-07-10 18:47:38 +00:00
transmatrix V = Id ;
2019-05-12 12:35:14 +00:00
2019-10-21 20:34:20 +00:00
if ( pmodel = = mdPixel ) {
2019-05-12 12:35:14 +00:00
V [ 0 ] [ 3 ] + = ( x - current_display - > xcenter ) ;
V [ 1 ] [ 3 ] + = ( y - current_display - > ycenter ) ;
2019-05-26 16:04:02 +00:00
V [ 0 ] [ 0 ] = size * 2 * cgi . hcrossf / cgi . crossf ;
V [ 1 ] [ 1 ] = size * 2 * cgi . hcrossf / cgi . crossf ;
2019-06-17 10:36:35 +00:00
if ( WDIM = = 3 ) V [ 2 ] [ 2 ] = - 1 ;
2019-05-12 12:35:14 +00:00
}
2020-09-11 09:18:02 +00:00
else if ( pmodel = = mdHorocyclic ) {
V [ 0 ] [ 3 ] + = ( x - current_display - > xcenter ) * 2 / current_display - > radius ;
V [ 1 ] [ 3 ] + = ( y - current_display - > ycenter ) * 2 / current_display - > radius ;
V [ 0 ] [ 0 ] = size * 2 / current_display - > radius ;
V [ 1 ] [ 1 ] = size * 2 / current_display - > radius ;
}
2019-05-12 12:35:14 +00:00
else {
V [ 0 ] [ 2 ] + = ( x - current_display - > xcenter ) ;
V [ 1 ] [ 2 ] + = ( y - current_display - > ycenter ) ;
2019-05-26 16:04:02 +00:00
V [ 0 ] [ 0 ] = size * 2 * cgi . hcrossf / cgi . crossf ;
V [ 1 ] [ 1 ] = size * 2 * cgi . hcrossf / cgi . crossf ;
2019-10-21 20:34:20 +00:00
V [ 2 ] [ 2 ] = current_display - > radius ;
2019-12-27 22:00:38 +00:00
if ( S3 > = OINF ) V [ 0 ] [ 0 ] / = 5 , V [ 1 ] [ 1 ] / = 5 ;
2019-05-12 12:35:14 +00:00
}
2017-07-10 18:47:38 +00:00
return V ;
}
2018-11-08 16:42:19 +00:00
void circle_around_center ( ld radius , color_t linecol , color_t fillcol , PPR prio ) {
2019-02-17 17:41:40 +00:00
# if CAP_QUEUE
2024-01-07 11:52:50 +00:00
if ( among ( pmodel , mdDisk , mdEquiarea , mdEquidistant , mdFisheye , mdFisheye2 ) & & ! ( pmodel = = mdDisk & & hyperbolic & & pconf . alpha < = - 1 ) & & models : : camera_straight ) {
2018-11-08 16:42:19 +00:00
hyperpoint ret ;
2020-07-27 16:49:04 +00:00
applymodel ( shiftless ( xpush0 ( radius ) ) , ret ) ;
2019-02-27 18:33:13 +00:00
ld r = hypot_d ( 2 , ret ) ;
2018-11-17 18:24:02 +00:00
queuecircle ( current_display - > xcenter , current_display - > ycenter , r * current_display - > radius , linecol , prio , fillcol ) ;
2018-11-08 16:42:19 +00:00
return ;
}
2019-02-17 17:41:40 +00:00
# endif
2019-02-17 18:39:44 +00:00
# if CAP_QUEUE
2023-04-11 14:44:11 +00:00
ld rad = 10 ;
if ( euclid ) rad = 1000 ;
2024-05-25 09:46:37 +00:00
for ( int i = 0 ; i < = 36000 ; i + = 10 ) curvepoint ( xspinpush0 ( i * degree / 100. , rad ) ) ;
2020-07-27 16:49:04 +00:00
auto & c = queuecurve ( shiftless ( Id ) , linecol , fillcol , prio ) ;
2020-04-16 22:53:58 +00:00
if ( pmodel = = mdDisk & & hyperbolic & & pconf . alpha < = - 1 )
2018-11-08 16:42:19 +00:00
c . flags | = POLY_FORCE_INVERTED ;
if ( pmodel = = mdJoukowsky )
c . flags | = POLY_FORCE_INVERTED ;
2018-11-08 18:44:08 +00:00
c . flags | = POLY_ALWAYS_IN ;
2024-05-25 09:46:37 +00:00
c . flags | = POLY_FORCEWIDE ;
2019-02-17 17:41:40 +00:00
# endif
2018-11-08 16:42:19 +00:00
}
2019-08-09 21:39:36 +00:00
EX color_t periodcolor = 0x00FF0080 ;
2020-09-23 16:53:06 +00:00
EX color_t ringcolor = 0xFFFF ;
2019-08-09 21:39:36 +00:00
EX color_t modelcolor = 0 ;
2023-05-15 00:26:02 +00:00
EX ld periodwidth = 1 ;
2018-11-08 16:42:19 +00:00
2023-03-16 13:44:31 +00:00
EX ld twopoint_xscale = 1 ;
EX ld twopoint_xwidth = 1 ;
EX int twopoint_xshape = 0 ;
EX void put_x ( shiftmatrix S , color_t col ) {
switch ( twopoint_xshape ) {
case 0 :
queuestr ( S * C0 , twopoint_xscale * vid . xres / 100 , " X " , ringcolor > > 8 ) ;
break ;
case 1 :
vid . linewidth * = twopoint_xwidth ;
queueline ( S * xpush0 ( twopoint_xscale / 10. ) , S * xpush0 ( - twopoint_xscale / 10. ) , ringcolor , 3 ) ;
queueline ( S * ypush0 ( twopoint_xscale / 10. ) , S * ypush0 ( - twopoint_xscale / 10. ) , ringcolor , 3 ) ;
vid . linewidth / = twopoint_xwidth ;
break ;
}
}
2019-02-17 18:39:44 +00:00
# if CAP_QUEUE
2019-08-09 19:00:52 +00:00
EX void draw_model_elements ( ) {
2018-11-08 16:42:19 +00:00
2020-12-27 16:37:39 +00:00
# if CAP_VR
2022-04-21 09:59:50 +00:00
if ( vrhr : : active ( ) & & models : : is_hyperboloid ( pmodel ) ) return ;
2020-12-27 16:37:39 +00:00
# endif
2020-12-27 16:14:50 +00:00
2023-10-05 11:58:13 +00:00
if ( sphere & & pconf . alpha < = 1 & & pmodel = = mdDisk )
queuecircle ( current_display - > xcenter , current_display - > ycenter , current_display - > xsize + current_display - > ysize , ringcolor , PPR : : OUTCIRCLE , modelcolor ) ;
2019-07-13 12:37:30 +00:00
dynamicval < ld > lw ( vid . linewidth , vid . linewidth * vid . multiplier_ring ) ;
2018-11-08 18:39:55 +00:00
switch ( pmodel ) {
2018-12-24 00:19:52 +00:00
2023-04-14 23:18:47 +00:00
# if MAXMDIM >= 4
2022-10-13 22:56:48 +00:00
case mdRelOrthogonal :
case mdRelPerspective : {
constexpr ld cc = 3 ;
if ( sl2 ) for ( ld dist : { - 0.1 , 0.1 } ) {
transmatrix Lorentz = Id ;
Lorentz [ 0 ] [ 0 ] = Lorentz [ 2 ] [ 2 ] = cosh ( cc ) ;
Lorentz [ 0 ] [ 2 ] = Lorentz [ 2 ] [ 0 ] = sinh ( cc ) ;
hyperpoint h = Lorentz * cspin ( 3 , 2 , dist ) * C0 ;
for ( int s = 0 ; s < = 360 ; s + + )
curvepoint ( spin ( s * degree ) * h ) ;
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : CIRCLE ) ;
}
if ( hyperbolic ) for ( ld dist : { - 0.1 , 0.1 } ) {
transmatrix Lorentz = Id ;
Lorentz [ 0 ] [ 0 ] = Lorentz [ 3 ] [ 3 ] = cosh ( cc ) ;
Lorentz [ 0 ] [ 3 ] = Lorentz [ 3 ] [ 0 ] = sinh ( cc ) ;
hyperpoint h = Lorentz * hyperpoint ( 0 , 0 , cosh ( dist ) , sinh ( dist ) ) ;
for ( int s = 0 ; s < = 360 ; s + + )
curvepoint ( spin ( s * degree ) * h ) ;
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : CIRCLE ) ;
}
return ;
}
2023-04-14 23:18:47 +00:00
# endif
2022-10-13 22:56:48 +00:00
2018-12-24 00:19:52 +00:00
case mdRotatedHyperboles : {
2020-04-16 22:53:58 +00:00
queuestr ( current_display - > xcenter , current_display - > ycenter + current_display - > radius * pconf . alpha , 0 , vid . fsize , " X " , ringcolor , 1 , 8 ) ;
2018-12-24 00:19:52 +00:00
return ;
}
2019-07-12 21:10:01 +00:00
case mdTwoHybrid : {
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , PPR : : CIRCLE ) ;
2019-07-12 21:10:01 +00:00
for ( int mode = 0 ; mode < 4 ; mode + + ) {
2019-07-13 12:37:47 +00:00
for ( int s = - 200 ; s < = 200 ; s + + ) {
ld p = tanh ( s / 40. ) ;
2020-04-16 22:53:58 +00:00
ld a = pconf . twopoint_param * ( 1 + p ) ;
ld b = pconf . twopoint_param * ( 1 - p ) ;
2019-07-12 21:10:01 +00:00
ld h = ( ( mode & 2 ) ? - 1 : 1 ) * sqrt ( asin_auto ( tan_auto ( a ) * tan_auto ( b ) ) ) ;
2020-04-16 22:53:58 +00:00
hyperpoint H = xpush ( p * pconf . twopoint_param ) * ypush0 ( h ) ;
2019-07-12 21:10:01 +00:00
hyperpoint res = compute_hybrid ( H , 2 | mode ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( res ) ;
2019-07-12 21:10:01 +00:00
curvepoint ( res * current_display - > radius ) ;
}
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : CIRCLE ) ;
2019-07-12 21:10:01 +00:00
}
queuereset ( pmodel , PPR : : CIRCLE ) ;
2021-03-12 14:31:00 +00:00
goto fallthrough ;
2019-07-12 21:10:01 +00:00
}
2018-11-08 16:42:19 +00:00
2021-03-12 14:31:00 +00:00
case mdTwoPoint : case mdSimulatedPerspective : fallthrough : {
2022-03-27 13:30:33 +00:00
if ( set_multi ) return ; /* no need */
2023-08-08 14:27:52 +00:00
auto T = rot_inverse ( pconf . mori ( ) . get ( ) ) ;
put_x ( shiftless ( T * xpush ( + pconf . twopoint_param ) ) , ringcolor > > 8 ) ;
put_x ( shiftless ( T * xpush ( - pconf . twopoint_param ) ) , ringcolor > > 8 ) ;
2018-11-08 18:39:55 +00:00
return ;
}
2021-03-06 10:54:25 +00:00
case mdThreePoint : {
vid . linewidth * = 5 ;
for ( int i = 0 ; i < = 3 ; i + + ) {
2022-11-12 21:38:45 +00:00
hyperpoint h = xspinpush0 ( 120. _deg * i , pconf . twopoint_param ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( h ) ;
2021-03-06 10:54:25 +00:00
curvepoint ( h ) ;
}
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : SUPERLINE ) ;
vid . linewidth / = 5 ;
return ;
}
2018-11-08 18:39:55 +00:00
case mdBall : {
2018-11-17 18:24:02 +00:00
queuecircle ( current_display - > xcenter , current_display - > ycenter , current_display - > radius , ringcolor , PPR : : OUTCIRCLE , modelcolor ) ;
2018-11-08 18:39:55 +00:00
ballgeometry ( ) ;
return ;
}
2022-04-07 18:50:16 +00:00
case mdHyperboloid :
case mdHemisphere : {
2020-11-01 19:10:08 +00:00
if ( ! pconf . show_hyperboloid_flat ) return ;
2022-04-07 18:50:16 +00:00
if ( models : : is_hyperboloid ( pmodel ) ) {
2024-06-27 20:32:09 +00:00
2018-11-08 16:42:19 +00:00
# if CAP_QUEUE
2024-06-27 20:32:09 +00:00
if ( pconf . small_hyperboloid ) queueaction ( PPR : : CIRCLE , [ ] { glflush ( ) ; pconf . small_hyperboloid = false ; } ) ;
2019-02-21 17:46:53 +00:00
curvepoint ( point3 ( 0 , 0 , 1 ) ) ;
2020-04-16 22:53:58 +00:00
curvepoint ( point3 ( 0 , 0 , - pconf . alpha ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : CIRCLE ) ;
2018-11-08 18:39:55 +00:00
2020-04-16 22:53:58 +00:00
ld & tz = pconf . top_z ;
2018-11-08 18:39:55 +00:00
ld z = acosh ( tz ) ;
2018-11-08 16:42:19 +00:00
2018-11-08 18:39:55 +00:00
hyperpoint a = xpush0 ( z ) ;
2023-08-14 14:18:44 +00:00
ld cb = pconf . ball ( ) [ 1 ] [ 1 ] ;
ld sb = pconf . ball ( ) [ 1 ] [ 2 ] ;
2024-06-28 12:01:16 +00:00
if ( pmodel = = mdHemisphere & & sphere ) cb = - cb ;
2018-11-08 18:39:55 +00:00
2024-06-27 20:32:32 +00:00
a [ 1 ] = sb * a [ 2 ] / cb ;
2018-11-08 18:39:55 +00:00
a [ 0 ] = sqrt ( - 1 + a [ 2 ] * a [ 2 ] - a [ 1 ] * a [ 1 ] ) ;
2020-04-16 22:53:58 +00:00
curvepoint ( point3 ( 0 , 0 , - pconf . alpha ) ) ;
2018-11-08 18:39:55 +00:00
curvepoint ( a ) ;
2019-02-21 17:46:53 +00:00
curvepoint ( point3 ( 0 , 0 , 0 ) ) ;
2018-11-08 18:39:55 +00:00
a [ 0 ] = - a [ 0 ] ;
curvepoint ( a ) ;
2020-04-16 22:53:58 +00:00
curvepoint ( point3 ( 0 , 0 , - pconf . alpha ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : CIRCLE ) ;
2018-11-08 18:39:55 +00:00
2019-02-21 17:46:53 +00:00
curvepoint ( point3 ( - 1 , 0 , 0 ) ) ;
curvepoint ( point3 ( 1 , 0 , 0 ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : CIRCLE ) ;
2018-11-08 18:39:55 +00:00
2024-06-27 20:32:32 +00:00
a [ 1 ] = sb * tz / cb ;
2018-11-08 18:39:55 +00:00
a [ 0 ] = sqrt ( tz * tz - a [ 1 ] * a [ 1 ] ) ;
2020-04-16 22:53:58 +00:00
a [ 2 ] = tz - pconf . alpha ;
2018-11-08 16:42:19 +00:00
2018-11-08 18:39:55 +00:00
curvepoint ( a ) ;
2020-04-16 22:53:58 +00:00
curvepoint ( point3 ( 0 , 0 , - pconf . alpha ) ) ;
2018-11-08 18:39:55 +00:00
a [ 0 ] = - a [ 0 ] ;
curvepoint ( a ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , ringcolor , 0 , PPR : : CIRCLE ) ;
2024-06-27 20:32:09 +00:00
if ( pconf . small_hyperboloid ) queueaction ( PPR : : CIRCLE , [ ] { glflush ( ) ; pconf . small_hyperboloid = true ; } ) ;
2018-11-08 16:42:19 +00:00
# endif
2018-11-08 18:39:55 +00:00
}
return ;
}
default : break ;
2018-11-08 16:42:19 +00:00
}
2018-11-08 18:39:55 +00:00
}
void queuestraight ( hyperpoint X , int style , color_t lc , color_t fc , PPR p ) {
2019-08-17 11:53:24 +00:00
hyperpoint H0 , H1 ;
2020-07-27 16:49:04 +00:00
applymodel ( shiftless ( X ) , H0 ) ;
2019-08-17 11:53:24 +00:00
H0 * = current_display - > radius ;
ld mul0 = hypot ( vid . xres , vid . yres ) / hypot_d ( 2 , H0 ) ;
2018-11-08 18:39:55 +00:00
2019-08-17 11:53:24 +00:00
if ( style = = 1 ) {
H1 = H0 * - mul0 ;
}
else {
2020-07-27 16:49:04 +00:00
applymodel ( shiftless ( pispin * X ) , H1 ) ;
2019-08-17 11:53:24 +00:00
H1 * = current_display - > radius ;
}
2018-11-08 18:39:55 +00:00
2019-08-17 11:53:24 +00:00
ld mul1 = hypot ( vid . xres , vid . yres ) / hypot_d ( 2 , H1 ) ;
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , p ) ;
2022-11-12 21:38:45 +00:00
curvepoint ( H0 + spin90 ( ) * H0 * mul0 ) ;
curvepoint ( H0 - spin90 ( ) * H0 * mul0 ) ;
curvepoint ( H1 + spin90 ( ) * H1 * mul1 ) ;
curvepoint ( H1 - spin90 ( ) * H1 * mul1 ) ;
curvepoint ( H0 + spin90 ( ) * H0 * mul0 ) ;
2019-08-17 11:53:24 +00:00
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , lc , fc , p ) . flags | = POLY_ALWAYS_IN | POLY_FORCEWIDE ;
2018-11-08 18:39:55 +00:00
queuereset ( pmodel , p ) ;
/*
for ( int i = 0 ; i < 1 ; i + + ) {
hyperpoint h = spin ( i * 45 * degree ) * X ;
hyperpoint res ;
applymodel ( h , res ) ;
if ( hypot2 ( res ) < 1000 & & ! std : : isnan ( res [ 0 ] ) & & ! std : : isnan ( res [ 1 ] ) )
2019-12-26 22:38:28 +00:00
queuestr ( h , 16 , " X " , 0xFF0000 + i * 0x20 ) ;
2018-11-08 18:39:55 +00:00
} */
2018-11-08 16:42:19 +00:00
}
2023-08-15 08:56:39 +00:00
/** ball is written as cspin(0, 1, alpha) * cspin(2, 1, beta) * cspin(0, 2, gamma) */
struct ball_deconstruct {
ld alpha , beta , gamma ;
transmatrix talpha , tbeta , tgamma , igamma ;
ld cos_beta , sin_beta ;
} ;
/** create a ball_deconstruct object */
ball_deconstruct deconstruct_ball ( ) {
// (0,1,0) -> (0, cos beta, sin beta) -> (sin alpha, cos beta * cos alpha, sin beta)
hyperpoint h = pconf . ball ( ) * point3 ( 0 , 1 , 0 ) ;
ball_deconstruct d ;
if ( h [ 0 ] = = 0 & & h [ 1 ] = = 0 ) { println ( hlog , " gimbal lock " ) ; return d ; }
d . alpha = atan2 ( h [ 0 ] , h [ 1 ] ) ;
d . beta = atan2 ( h [ 2 ] , hypot ( h [ 0 ] , h [ 1 ] ) ) ;
d . cos_beta = cos ( d . beta ) ;
d . sin_beta = sin ( d . beta ) ;
d . talpha = cspin ( 0 , 1 , d . alpha ) ;
d . tbeta = cspin ( 2 , 1 , d . beta ) ;
d . tgamma = rot_inverse ( d . tbeta ) * rot_inverse ( d . talpha ) * pconf . ball ( ) ;
h = d . tgamma * point3 ( 0 , 0 , 1 ) ;
d . gamma = atan2 ( h [ 0 ] , h [ 2 ] ) ;
if ( ! eqmatrix ( d . tgamma , cspin ( 0 , 2 , d . gamma ) ) ) println ( hlog , " deconstruction failed " ) ;
d . igamma = cspin ( 1 , 0 , d . gamma ) ;
return d ;
}
2019-08-09 19:00:52 +00:00
EX void draw_boundary ( int w ) {
2018-11-08 16:42:19 +00:00
2023-07-21 07:29:49 +00:00
if ( ( nonisotropic | | gproduct ) & & pmodel = = mdDisk ) {
queuecircle ( current_display - > xcenter , current_display - > ycenter , current_display - > radius , ringcolor , PPR : : OUTCIRCLE , modelcolor ) ;
return ;
}
2018-11-08 16:42:19 +00:00
if ( w = = 1 ) return ;
2024-01-07 11:52:50 +00:00
if ( nonisotropic | | ( euclid & & ! among ( pmodel , mdFisheye , mdFisheye2 , mdConformalSquare , mdHemisphere ) ) | | gproduct ) return ;
2020-12-27 16:37:39 +00:00
# if CAP_VR
2020-12-30 13:20:30 +00:00
if ( vrhr : : active ( ) & & pmodel = = mdHyperboloid ) return ;
2020-12-27 16:37:39 +00:00
# endif
2018-11-08 16:42:19 +00:00
2019-07-13 12:37:30 +00:00
dynamicval < ld > lw ( vid . linewidth , vid . linewidth * vid . multiplier_ring ) ;
2018-11-08 16:42:19 +00:00
color_t lc = ringcolor ;
2018-11-08 18:39:55 +00:00
color_t fc = modelcolor ;
2018-11-08 16:42:19 +00:00
PPR p = PPR : : OUTCIRCLE ;
if ( haveaura ( ) ) lc = 0 ;
if ( lc = = 0 & & fc = = 0 ) return ;
2018-12-24 00:19:52 +00:00
if ( pmodel = = mdRotatedHyperboles ) return ;
2018-11-08 18:39:55 +00:00
ld fakeinf = sphere ? M_PI - 1e-5 : hyperbolic ? 10 : exp ( 10 ) ;
2018-12-13 16:03:39 +00:00
2018-12-15 14:17:06 +00:00
# if CAP_SVG
2018-12-13 16:03:39 +00:00
dynamicval < ld > dw ( vid . linewidth , vid . linewidth * ( svg : : in ? svg : : divby : 1 ) ) ;
2018-12-15 14:17:06 +00:00
# endif
2018-11-08 16:42:19 +00:00
2023-05-15 00:26:02 +00:00
if ( elliptic & & ! among ( pmodel , mdBand , mdBandEquidistant , mdBandEquiarea , mdSinusoidal , mdMollweide , mdCollignon ) ) {
dynamicval < ld > d ( vid . linewidth , vid . linewidth * periodwidth ) ;
2022-11-12 21:38:45 +00:00
circle_around_center ( 90. _deg , periodcolor , 0 , PPR : : CIRCLE ) ;
2023-05-15 00:26:02 +00:00
}
2018-11-08 18:39:55 +00:00
2020-09-15 17:17:07 +00:00
int broken_coord = models : : get_broken_coord ( pmodel ) ;
if ( broken_coord ) {
2023-05-15 00:26:02 +00:00
dynamicval < ld > d ( vid . linewidth , vid . linewidth * periodwidth ) ;
2020-09-15 17:17:07 +00:00
int unbroken_coord = 3 - broken_coord ;
const ld eps = 1e-3 ;
const ld rem = sqrt ( 1 - eps * eps ) ;
for ( int s : { - 1 , 1 } ) {
for ( int a = 1 ; a < 180 ; a + + ) {
hyperpoint h = Hypc ;
h [ broken_coord ] = - sin_auto ( a * degree ) * rem ;
h [ 0 ] = sin_auto ( a * degree ) * eps * s ;
h [ unbroken_coord ] = cos_auto ( a * degree ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( h ) ;
2020-09-15 17:17:07 +00:00
curvepoint ( h ) ;
}
queuecurve ( shiftless ( Id ) , periodcolor , 0 , PPR : : CIRCLE ) . flags | = POLY_FORCEWIDE ;
}
}
2020-09-23 12:56:26 +00:00
if ( pmodel = = mdWerner & & hyperbolic ) return ;
2018-11-08 16:42:19 +00:00
switch ( pmodel ) {
case mdTwoPoint : {
2024-01-07 08:07:54 +00:00
if ( twopoint_do_flips | | current_display - > separate_eyes ( ) | | ! sphere ) return ;
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , p ) ;
2018-11-08 16:42:19 +00:00
for ( int b = - 1 ; b < = 1 ; b + = 2 )
for ( ld a = - 90 ; a < = 90 + 1e-6 ; a + = pow ( .5 , vid . linequality ) ) {
2020-04-16 22:53:58 +00:00
ld x = sin ( a * pconf . twopoint_param * b / 90 ) ;
2018-11-08 16:42:19 +00:00
ld y = 0 ;
ld z = - sqrt ( 1 - x * x ) ;
2023-08-08 14:27:52 +00:00
hyperpoint h0 = hpxyz ( x , y , z ) ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( h0 ) ;
2018-11-08 16:42:19 +00:00
hyperpoint h1 ;
2023-08-08 14:27:52 +00:00
applymodel ( shiftless ( h0 ) , h1 ) ;
2018-11-08 16:42:19 +00:00
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( h1 ) ;
2018-11-08 16:42:19 +00:00
h1 [ 1 ] = abs ( h1 [ 1 ] ) * b ;
2023-08-14 09:08:37 +00:00
models : : ori_to_scr ( h1 ) ;
2018-11-08 16:42:19 +00:00
curvepoint ( h1 ) ;
}
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , lc , fc , p ) . flags | = POLY_FORCEWIDE ;
2018-11-08 16:42:19 +00:00
queuereset ( pmodel , p ) ;
return ;
}
2020-09-15 17:17:07 +00:00
case mdBand : case mdBandEquidistant : case mdBandEquiarea : case mdSinusoidal : case mdMollweide : case mdCentralCyl : case mdCollignon :
case mdGallStereographic : case mdMiller :
{
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) return ;
2020-04-16 22:53:58 +00:00
if ( pmodel = = mdBand & & pconf . model_transition ! = 1 ) return ;
2020-09-15 17:17:07 +00:00
bool bndband = ( among ( pmodel , mdBand , mdMiller , mdGallStereographic , mdCentralCyl ) ? hyperbolic : sphere ) ;
2023-08-15 13:45:43 +00:00
transmatrix T = pconf . mori ( ) . get ( ) ;
2022-11-12 21:38:45 +00:00
ld right = 90. _deg - 1e-5 ;
2018-11-08 18:39:55 +00:00
if ( bndband )
queuestraight ( T * ypush0 ( hyperbolic ? 10 : right ) , 2 , lc , fc , p ) ;
ld xperiod = elliptic ? fakeinf / 2 : fakeinf ;
if ( sphere & & ! bndband ) {
2023-05-15 00:26:02 +00:00
dynamicval < ld > d ( vid . linewidth , vid . linewidth * periodwidth ) ;
2018-11-08 18:39:55 +00:00
queuestraight ( T * xpush0 ( xperiod ) , 2 , periodcolor , 0 , PPR : : CIRCLE ) ;
}
if ( sphere & & bndband ) {
2023-05-15 00:26:02 +00:00
dynamicval < ld > d ( vid . linewidth , vid . linewidth * periodwidth ) ;
2018-11-08 18:39:55 +00:00
ld adegree = degree - 1e-6 ;
for ( ld a = - 90 ; a < 90 + 1e-6 ; a + = pow ( .5 , vid . linequality ) ) {
curvepoint ( T * xpush ( xperiod ) * ypush0 ( a * adegree ) ) ;
}
for ( ld a = - 90 ; a < 90 + 1e-6 ; a + = pow ( .5 , vid . linequality ) ) {
curvepoint ( T * xpush ( - xperiod ) * ypush0 ( - a * adegree ) ) ;
}
curvepoint ( T * xpush ( xperiod ) * ypush0 ( - 90 * adegree ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , periodcolor , 0 , PPR : : CIRCLE ) . flags | = POLY_FORCEWIDE ;
2018-11-08 16:42:19 +00:00
}
2018-11-08 18:39:55 +00:00
return ;
2018-11-08 16:42:19 +00:00
}
2018-11-08 18:39:55 +00:00
case mdHalfplane :
2019-08-15 13:05:43 +00:00
if ( hyperbolic & & GDIM = = 2 ) {
2023-08-14 09:24:29 +00:00
transmatrix Ori = pconf . mori ( ) . get ( ) ;
2023-08-08 14:27:52 +00:00
queuestraight ( Ori * spin270 ( ) * xpush0 ( fakeinf ) , 1 , lc , fc , p ) ;
2018-11-08 18:39:55 +00:00
return ;
}
break ;
2018-11-10 17:11:10 +00:00
2018-11-11 00:37:24 +00:00
case mdHemisphere : {
2023-08-15 08:56:39 +00:00
auto d = deconstruct_ball ( ) ;
2018-11-11 00:37:24 +00:00
if ( hyperbolic ) {
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , p ) ;
2018-11-11 00:37:24 +00:00
for ( int i = 0 ; i < = 360 ; i + + ) {
2023-08-15 08:56:39 +00:00
ld c1 = cos ( i * degree - d . gamma ) ;
ld s1 = sin ( i * degree - d . gamma ) ;
curvepoint ( point3 ( current_display - > radius * c1 , current_display - > radius * s1 * ( d . cos_beta * s1 > = 0 - 1e-6 ? 1 : abs ( d . sin_beta ) ) , 0 ) ) ;
2018-11-11 00:37:24 +00:00
}
2023-08-15 12:29:42 +00:00
queuecurve_reuse ( shiftless ( d . talpha ) , lc , fc , p ) ;
2018-11-11 00:37:24 +00:00
queuereset ( pmodel , p ) ;
2023-08-15 08:56:39 +00:00
2018-11-11 00:37:24 +00:00
p = PPR : : CIRCLE ; fc = 0 ;
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , p ) ;
2023-08-15 12:29:42 +00:00
queuecurve ( shiftless ( d . talpha ) , lc , fc , p ) ;
2018-11-11 00:37:24 +00:00
for ( int i = 0 ; i < = 360 ; i + + ) {
2023-08-15 08:56:39 +00:00
ld c = cos ( i * degree ) ;
2018-11-11 00:37:24 +00:00
ld s = sin ( i * degree ) ;
2023-08-15 08:56:39 +00:00
curvepoint ( point3 ( current_display - > radius * c , current_display - > radius * s * d . sin_beta , 0 ) ) ;
2018-11-11 00:37:24 +00:00
}
2023-08-15 08:56:39 +00:00
queuecurve ( shiftless ( d . talpha ) , lc , fc , p ) ;
2018-11-11 00:37:24 +00:00
queuereset ( pmodel , p ) ;
}
2022-04-07 18:50:16 +00:00
if ( euclid ) {
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , p ) ;
2018-11-11 00:37:24 +00:00
for ( int i = 0 ; i < = 360 ; i + + ) {
2019-02-21 17:46:53 +00:00
curvepoint ( point3 ( current_display - > radius * cos ( i * degree ) , current_display - > radius * sin ( i * degree ) , 0 ) ) ;
2018-11-11 00:37:24 +00:00
}
2023-08-15 12:33:53 +00:00
queuecurve_reuse ( shiftless ( Id ) , lc , fc , p ) ;
2018-11-11 00:37:24 +00:00
queuereset ( pmodel , p ) ;
2023-08-15 12:33:53 +00:00
queuereset ( mdPixel , PPR : : CIRCLE ) ;
queuecurve ( shiftless ( Id ) , lc , 0 , PPR : : CIRCLE ) ;
queuereset ( pmodel , PPR : : CIRCLE ) ;
2018-11-11 00:37:24 +00:00
}
2022-04-07 18:50:16 +00:00
if ( sphere ) goto as_hyperboloid ;
2018-11-11 00:37:24 +00:00
return ;
}
case mdHyperboloid : {
if ( hyperbolic ) {
2022-04-07 18:50:16 +00:00
as_hyperboloid :
2023-08-15 08:56:39 +00:00
auto d = deconstruct_ball ( ) ;
2020-04-16 22:53:58 +00:00
ld & tz = pconf . top_z ;
2023-08-15 12:21:19 +00:00
ld mz = acosh ( tz ) ;
for ( int it = 0 ; it < ( sphere ? 2 : 1 ) ; it + + ) {
auto fc1 = fc ;
auto p1 = p ;
2018-11-11 00:37:24 +00:00
2023-08-15 12:21:19 +00:00
if ( abs ( d . sin_beta ) < = abs ( d . cos_beta ) + 1e-5 ) {
queuereset ( mdPixel , p1 ) ;
int steps = 100 < < vid . linequality ;
hyperpoint a ;
auto hpolar = [ ] ( ld phi , ld r ) {
ld s = sinh ( r ) ;
return point3 ( cos ( phi ) * s , - sin ( phi ) * s , cosh ( r ) ) ;
} ;
auto hform = [ & ] ( hyperpoint h ) {
h = hyperboloid_form ( d . igamma * h ) ;
h [ 0 ] * = current_display - > radius ; h [ 1 ] * = current_display - > radius ; h [ 2 ] = 0 ;
if ( it ) h [ 0 ] * = - 1 , h [ 1 ] * = - 1 ;
return h ;
} ;
for ( int ts = - steps ; ts < = steps ; ts + + ) {
ld t = ts * 1. / steps ;
a = hpolar ( 0 , t * mz ) ;
if ( t ! = 0 ) {
a [ 1 ] = d . sin_beta * a [ 2 ] / - d . cos_beta ;
ld v = - 1 + a [ 2 ] * a [ 2 ] - a [ 1 ] * a [ 1 ] ;
if ( v < 0 ) continue ;
a [ 0 ] = sqrt ( v ) ;
if ( t < 0 ) a [ 0 ] = - a [ 0 ] ;
}
curvepoint ( hform ( a ) ) ;
2018-11-11 00:37:24 +00:00
}
2023-08-15 12:21:19 +00:00
if ( ( d . sin_beta > 0 ) ^ ( d . cos_beta < 0 ) ) {
ld alpha = ( M_PI - atan2 ( a [ 0 ] , - a [ 1 ] ) ) / steps ;
for ( int ts = - steps ; ts < = steps ; ts + + )
curvepoint ( hform ( hpolar ( - 90. _deg - ts * alpha , mz ) ) ) ;
}
else {
ld alpha = - atan2 ( a [ 0 ] , - a [ 1 ] ) / steps ;
for ( int ts = - steps ; ts < = steps ; ts + + )
curvepoint ( hform ( hpolar ( + 90. _deg - ts * alpha , mz ) ) ) ;
}
2018-11-11 00:37:24 +00:00
2023-08-15 12:29:42 +00:00
queuecurve_reuse ( shiftless ( Id ) , lc , fc1 , p1 ) ;
2023-08-15 12:21:19 +00:00
queuereset ( pmodel , p1 ) ;
fc1 = 0 ; p1 = PPR : : CIRCLE ;
2023-08-15 12:29:42 +00:00
queuereset ( mdPixel , p1 ) ;
queuecurve ( shiftless ( Id ) , lc , fc1 , p1 ) ;
queuereset ( pmodel , p1 ) ;
2018-11-11 00:37:24 +00:00
}
2022-04-07 18:50:16 +00:00
2024-06-27 20:32:09 +00:00
int mul = pconf . small_hyperboloid ? 2 : 1 ;
2022-04-07 18:50:16 +00:00
for ( ld t = 0 ; t < = 360 ; t + + )
2024-06-27 20:32:09 +00:00
curvepoint ( xspinpush0 ( t * degree , it ? M_PI - mz * mul : mz * mul ) ) ;
2022-04-07 18:50:16 +00:00
2023-08-15 12:29:42 +00:00
if ( p1 = = PPR : : OUTCIRCLE ) { queuecurve_reuse ( shiftless ( Id ) , lc , fc1 , p1 ) ; fc1 = 0 ; p1 = PPR : : CIRCLE ; }
2023-08-15 12:21:19 +00:00
queuecurve ( shiftless ( Id ) , lc , fc1 , p1 ) ;
2022-04-07 18:50:16 +00:00
}
2018-11-11 00:37:24 +00:00
}
2023-08-15 12:21:19 +00:00
else if ( sphere ) {
2023-08-15 09:00:43 +00:00
queuereset ( mdPixel , p ) ;
for ( int i = 0 ; i < = 360 ; i + + ) {
curvepoint ( point3 ( current_display - > radius * cos ( i * degree ) / 3 , current_display - > radius * sin ( i * degree ) / 3 , 0 ) ) ;
}
2023-08-15 12:29:42 +00:00
queuecurve_reuse ( shiftless ( Id ) , lc , fc , p ) ;
2023-08-15 09:00:43 +00:00
queuereset ( pmodel , p ) ;
2023-08-15 12:29:42 +00:00
queuereset ( mdPixel , PPR : : CIRCLE ) ;
queuecurve ( shiftless ( Id ) , lc , 0 , PPR : : CIRCLE ) ;
queuereset ( pmodel , PPR : : CIRCLE ) ;
2023-08-15 09:00:43 +00:00
}
2018-11-11 00:37:24 +00:00
return ;
}
2018-11-10 17:11:10 +00:00
case mdSpiral : {
if ( euclid ) return ;
2019-08-09 22:58:50 +00:00
if ( models : : ring_not_spiral ) return ;
2018-11-10 17:32:29 +00:00
// if(p == PPR::CIRCLE) p = PPR::OUTCIRCLE;
2019-08-09 22:58:50 +00:00
auto & sm = models : : spiral_multiplier ;
2018-11-10 17:32:29 +00:00
ld u = hypot ( 1 , imag ( sm ) / real ( sm ) ) ;
2018-11-10 17:11:10 +00:00
if ( real ( sm ) ) {
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , p ) ;
2018-11-10 17:32:29 +00:00
for ( ld a = - 10 ; a < = 10 ; a + = 0.01 / ( 1 < < vid . linequality ) / u ) {
2018-11-10 17:11:10 +00:00
cld z = exp ( cld ( a , a * imag ( sm ) / real ( sm ) + M_PI ) ) ;
2019-02-21 17:46:53 +00:00
hyperpoint ret = point2 ( real ( z ) , imag ( z ) ) ;
2020-04-16 22:53:58 +00:00
ret = mobius ( ret , pconf . skiprope , 1 ) ;
2018-11-17 18:24:02 +00:00
ret * = current_display - > radius ;
2024-05-16 19:16:10 +00:00
models : : ori_to_scr ( ret ) ;
2018-11-10 17:11:10 +00:00
curvepoint ( ret ) ;
}
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , ringcolor , 0 , p ) . flags | = POLY_ALWAYS_IN ;
2018-11-10 17:11:10 +00:00
queuereset ( pmodel , p ) ;
}
return ;
}
2023-08-15 12:39:52 +00:00
case mdBall :
/* circle_around_center not wanted */
return ;
2018-11-08 18:39:55 +00:00
2023-02-03 15:20:26 +00:00
default :
if ( models : : is_perspective ( pmodel ) ) return ;
break ;
2018-11-08 16:42:19 +00:00
}
2020-04-16 22:53:58 +00:00
if ( sphere & & pmodel = = mdDisk & & pconf . alpha > 1 ) {
double rad = current_display - > radius / sqrt ( pconf . alpha * pconf . alpha - 1 ) ;
2018-11-17 18:24:02 +00:00
queuecircle ( current_display - > xcenter , current_display - > ycenter , rad , lc , p , fc ) ;
2018-11-08 16:42:19 +00:00
return ;
}
2018-11-11 00:37:24 +00:00
2018-11-08 18:39:55 +00:00
if ( sphere & & ! among ( pmodel , mdEquidistant , mdEquiarea ) ) return ;
circle_around_center ( fakeinf , lc , fc , p ) ;
2018-11-08 16:42:19 +00:00
}
2019-02-17 17:41:40 +00:00
# endif
2018-11-08 16:42:19 +00:00
2020-07-27 16:49:04 +00:00
EX void change_shift ( shiftpoint & h , ld by ) {
2020-09-16 12:23:00 +00:00
if ( ! by ) return ;
2020-07-27 16:49:04 +00:00
h . shift + = by ;
if ( sl2 ) {
ld ca = cos ( by ) , sa = sin ( by ) ;
tie ( h [ 2 ] , h [ 3 ] ) = make_pair ( h [ 2 ] * ca - h [ 3 ] * sa , h [ 3 ] * ca + h [ 2 ] * sa ) ;
tie ( h [ 0 ] , h [ 1 ] ) = make_pair ( h [ 0 ] * ca - h [ 1 ] * sa , h [ 1 ] * ca + h [ 0 ] * sa ) ;
}
2022-09-17 14:46:26 +00:00
else if ( ( mdinf [ pmodel ] . flags & mf : : uses_bandshift ) | | ( sphere & & pmodel = = mdSpiral ) ) {
2023-08-14 09:25:09 +00:00
models : : scr_to_ori ( h . h ) ;
2023-08-15 13:49:10 +00:00
h . h = xpush ( - by ) * h . h ;
models : : ori_to_scr ( h . h ) ;
2022-09-17 14:46:26 +00:00
}
2020-07-27 16:49:04 +00:00
}
EX void change_shift ( shiftmatrix & T , ld by ) {
2020-09-16 12:23:00 +00:00
if ( ! by ) return ;
2020-07-27 16:49:04 +00:00
T . shift + = by ;
if ( sl2 ) {
ld ca = cos ( by ) , sa = sin ( by ) ;
for ( int a = 0 ; a < 4 ; a + + ) {
tie ( T [ 2 ] [ a ] , T [ 3 ] [ a ] ) = make_pair ( T [ 2 ] [ a ] * ca - T [ 3 ] [ a ] * sa , T [ 3 ] [ a ] * ca + T [ 2 ] [ a ] * sa ) ;
tie ( T [ 0 ] [ a ] , T [ 1 ] [ a ] ) = make_pair ( T [ 0 ] [ a ] * ca - T [ 1 ] [ a ] * sa , T [ 1 ] [ a ] * ca + T [ 0 ] [ a ] * sa ) ;
}
}
2022-09-17 14:46:26 +00:00
else if ( ( mdinf [ pmodel ] . flags & mf : : uses_bandshift ) | | ( sphere & & pmodel = = mdSpiral ) ) {
2023-08-14 09:25:09 +00:00
models : : scr_to_ori ( T . T ) ;
2022-09-17 14:46:26 +00:00
T . T = xpush ( - by ) * T . T ;
fixmatrix ( T . T ) ;
2023-08-14 09:25:09 +00:00
models : : ori_to_scr ( T . T ) ;
2022-09-17 14:46:26 +00:00
}
2020-07-27 16:49:04 +00:00
}
EX transmatrix unshift ( shiftmatrix T , ld to IS ( 0 ) ) {
change_shift ( T , to - T . shift ) ;
return T . T ;
}
EX hyperpoint unshift ( shiftpoint T , ld to IS ( 0 ) ) {
change_shift ( T , to - T . shift ) ;
return T . h ;
}
EX transmatrix inverse_shift ( const shiftmatrix & T1 , const shiftmatrix & T2 ) {
2020-09-16 03:57:05 +00:00
return iso_inverse ( T1 . T ) * unshift ( T2 , T1 . shift ) ;
2020-07-27 16:49:04 +00:00
}
EX hyperpoint inverse_shift ( const shiftmatrix & T1 , const shiftpoint & T2 ) {
2020-09-16 03:57:05 +00:00
return iso_inverse ( T1 . T ) * unshift ( T2 , T1 . shift ) ;
2020-07-27 16:49:04 +00:00
}
2022-06-16 21:14:10 +00:00
EX void optimize_shift ( shiftpoint & h ) {
if ( sl2 ) {
change_shift ( h , atan2 ( h [ 2 ] , h [ 3 ] ) ) ;
}
}
2020-07-27 16:49:04 +00:00
EX void optimize_shift ( shiftmatrix & T ) {
2022-09-17 14:46:26 +00:00
if ( sl2 ) {
change_shift ( T , atan2 ( T [ 2 ] [ 3 ] , T [ 3 ] [ 3 ] ) ) ;
if ( hybrid : : csteps ) {
auto period = ( M_PI * hybrid : : csteps ) / cgi . psl_steps ;
while ( T . shift > period * .4999 )
T . shift - = period ;
while ( T . shift < - period * .5001 )
T . shift + = period ;
}
return ;
}
else if ( ( ( mdinf [ pmodel ] . flags & mf : : uses_bandshift ) & & T [ LDIM ] [ LDIM ] > 30 ) | | ( sphere & & pmodel = = mdSpiral ) ) {
2023-08-15 13:49:10 +00:00
models : : scr_to_ori ( T . T ) ;
2020-07-27 16:49:04 +00:00
hyperpoint H = tC0 ( T . T ) ;
2018-11-09 13:14:36 +00:00
find_zlev ( H ) ;
ld y = asin_auto ( H [ 1 ] ) ;
ld x = asin_auto_clamp ( H [ 0 ] / cos_auto ( y ) ) ;
2019-04-03 18:30:35 +00:00
if ( sphere ) {
2019-08-17 21:28:41 +00:00
if ( H [ LDIM ] < 0 & & x > 0 ) x = M_PI - x ;
else if ( H [ LDIM ] < 0 & & x < = 0 ) x = - M_PI - x ;
2019-04-03 18:30:35 +00:00
}
2020-07-27 16:49:04 +00:00
T . shift + = x ;
T . T = xpush ( - x ) * T . T ;
fixmatrix ( T . T ) ;
2023-08-15 13:49:10 +00:00
models : : ori_to_scr ( T . T ) ;
2020-07-27 16:49:04 +00:00
}
2018-11-09 13:14:36 +00:00
}
2020-07-27 16:49:04 +00:00
EX shiftmatrix optimized_shift ( const shiftmatrix & T ) {
shiftmatrix U = T ;
optimize_shift ( U ) ;
return U ;
}
2019-08-09 19:00:52 +00:00
2019-08-09 20:37:11 +00:00
EX namespace dq {
2020-07-27 16:49:04 +00:00
EX queue < pair < heptagon * , shiftmatrix > > drawqueue ;
EX unsigned bucketer ( const shiftpoint & T ) {
2023-01-26 23:27:10 +00:00
if ( cgi . emb - > is_euc_in_sl2 ( ) ) {
2023-01-07 11:55:28 +00:00
auto T1 = T ; optimize_shift ( T1 ) ;
return bucketer ( T1 . h ) + unsigned ( floor ( T1 . shift * 81527 + .5 ) ) ;
}
2020-07-27 16:49:04 +00:00
return bucketer ( T . h ) + unsigned ( floor ( T . shift * 81527 + .5 ) ) ;
}
2018-11-10 13:26:49 +00:00
2019-08-09 20:37:11 +00:00
EX set < heptagon * > visited ;
2020-07-27 16:49:04 +00:00
EX void enqueue ( heptagon * h , const shiftmatrix & T ) {
2018-11-10 13:26:49 +00:00
if ( ! h | | visited . count ( h ) ) { return ; }
visited . insert ( h ) ;
2020-07-27 16:49:04 +00:00
drawqueue . emplace ( h , T ) ;
2018-11-10 13:26:49 +00:00
}
2020-07-27 16:49:04 +00:00
EX set < unsigned > visited_by_matrix ;
EX void enqueue_by_matrix ( heptagon * h , const shiftmatrix & T ) {
2019-03-15 12:46:23 +00:00
if ( ! h ) return ;
2023-01-04 22:30:36 +00:00
unsigned b = bucketer ( T * tile_center ( ) ) ;
2019-03-15 12:46:23 +00:00
if ( visited_by_matrix . count ( b ) ) { return ; }
visited_by_matrix . insert ( b ) ;
2020-07-27 16:49:04 +00:00
drawqueue . emplace ( h , T ) ;
2019-03-15 12:46:23 +00:00
}
2019-12-06 11:13:09 +00:00
2020-07-27 16:49:04 +00:00
EX queue < pair < cell * , shiftmatrix > > drawqueue_c ;
2020-04-05 08:55:40 +00:00
EX set < cell * > visited_c ;
2020-07-27 16:49:04 +00:00
EX void enqueue_c ( cell * c , const shiftmatrix & T ) {
2020-04-05 08:55:40 +00:00
if ( ! c | | visited_c . count ( c ) ) { return ; }
visited_c . insert ( c ) ;
2020-07-27 16:49:04 +00:00
drawqueue_c . emplace ( c , T ) ;
2020-04-05 08:55:40 +00:00
}
2019-12-06 11:13:09 +00:00
2020-07-27 16:49:04 +00:00
EX void enqueue_by_matrix_c ( cell * c , const shiftmatrix & T ) {
2019-12-06 11:13:09 +00:00
if ( ! c ) return ;
2023-01-04 22:30:36 +00:00
unsigned b = bucketer ( T * tile_center ( ) ) ;
2019-12-06 11:13:09 +00:00
if ( visited_by_matrix . count ( b ) ) { return ; }
visited_by_matrix . insert ( b ) ;
2020-07-27 16:49:04 +00:00
drawqueue_c . emplace ( c , T ) ;
2019-12-06 11:13:09 +00:00
}
2020-05-27 23:50:00 +00:00
EX void clear_all ( ) {
visited . clear ( ) ;
visited_by_matrix . clear ( ) ;
visited_c . clear ( ) ;
2020-05-29 08:49:38 +00:00
while ( ! drawqueue_c . empty ( ) ) drawqueue_c . pop ( ) ;
while ( ! drawqueue . empty ( ) ) drawqueue . pop ( ) ;
2020-05-27 23:50:00 +00:00
}
2019-12-06 11:13:09 +00:00
2019-08-09 20:37:11 +00:00
EX }
2018-11-10 13:26:49 +00:00
2019-08-09 19:00:52 +00:00
EX bool do_draw ( cell * c ) {
2018-11-10 13:26:49 +00:00
// do not display out of range cells, unless on torus
2022-12-29 20:49:43 +00:00
if ( c - > pathdist = = PINFD & & ! ( meuclid & & quotient ) & & vid . use_smart_range = = 0 )
2018-11-10 13:26:49 +00:00
return false ;
2019-02-09 11:23:45 +00:00
// do not display not fully generated cells, unless changing range allowed
if ( c - > mpdist > 7 & & ! allowChangeRange ( ) ) return false ;
2018-11-10 13:26:49 +00:00
// in the Yendor Challenge, scrolling back is forbidden
2022-02-21 21:47:22 +00:00
if ( c - > cpdist > get_sightrange ( ) & & ( yendor : : on | | isHaunted ( cwt . at - > land ) ) & & ! cheater & & ! autocheat ) return false ;
2018-11-10 13:26:49 +00:00
return true ;
}
2019-08-09 19:00:52 +00:00
EX ld extra_generation_distance = 99 ;
2019-03-08 21:42:14 +00:00
2019-05-15 13:49:09 +00:00
// returns false if limited
2024-03-14 18:28:44 +00:00
EX bool limited_generation ( cell * c ) {
2019-05-15 13:49:09 +00:00
if ( c - > mpdist < = 7 ) return true ;
if ( cells_generated > vid . cells_generated_limit ) return false ;
setdist ( c , 7 , c ) ;
cells_generated + + ;
return true ;
}
2023-01-04 22:26:46 +00:00
EX int min_cells_drawn = 50 ;
2024-03-14 18:28:44 +00:00
EX hookset < int ( cell * , const shiftmatrix & ) > hooks_do_draw ;
2020-07-27 16:49:04 +00:00
EX bool do_draw ( cell * c , const shiftmatrix & T ) {
2024-03-14 18:28:44 +00:00
int h = callhandlers ( 0 , hooks_do_draw , c , T ) ;
if ( h ) return h > 0 ;
2019-07-28 09:17:25 +00:00
2019-05-08 16:33:08 +00:00
if ( WDIM = = 3 ) {
2024-10-05 11:45:16 +00:00
if ( models : : conformal_product_model ( ) ) {
ld z = zlevel ( T . T * C0 ) ;
if ( z > M_PI + 0.01 | | z < = 0.01 - M_PI ) return false ;
}
2019-10-25 23:09:25 +00:00
// do not care about cells outside of the track
if ( GDIM = = 3 & & racing : : on & & c - > land = = laMemory & & cells_drawn > = S7 + 1 ) return false ;
2019-03-10 11:12:47 +00:00
if ( cells_drawn > vid . cells_drawn_limit ) return false ;
2023-01-04 22:26:46 +00:00
if ( cells_drawn < min_cells_drawn ) { limited_generation ( c ) ; return true ; }
2021-03-30 09:27:48 +00:00
# if MAXMDIM >= 4
2022-10-15 20:58:30 +00:00
if ( nil & & models : : is_perspective ( pmodel ) ) {
2020-04-11 13:08:24 +00:00
ld dist = hypot_d ( 3 , inverse_exp ( tC0 ( T ) , pQUICK ) ) ;
2019-08-06 19:01:00 +00:00
if ( dist > sightranges [ geometry ] + ( vid . sloppy_3d ? 0 : 0.9 ) ) return false ;
if ( dist < = extra_generation_distance & & ! limited_generation ( c ) ) return false ;
2019-08-06 10:00:46 +00:00
}
2022-10-15 20:58:30 +00:00
else if ( sol & & models : : is_perspective ( pmodel ) ) {
2020-07-27 16:49:04 +00:00
if ( ! nisot : : in_table_range ( tC0 ( T . T ) ) ) return false ;
2019-07-30 11:02:44 +00:00
if ( ! limited_generation ( c ) ) return false ;
}
2022-10-15 20:58:30 +00:00
else if ( nih & & models : : is_perspective ( pmodel ) ) {
2020-04-11 13:08:24 +00:00
hyperpoint h = inverse_exp ( tC0 ( T ) , pQUICK ) ;
2019-10-01 03:03:46 +00:00
ld dist = hypot_d ( 3 , h ) ;
if ( dist > sightranges [ geometry ] + ( vid . sloppy_3d ? 0 : cgi . corner_bonus ) ) return false ;
if ( dist < = extra_generation_distance & & ! limited_generation ( c ) ) return false ;
}
2022-10-15 20:58:30 +00:00
else if ( sl2 & & models : : is_perspective ( pmodel ) ) {
if ( hypot ( tC0 ( T . T ) [ 2 ] , tC0 ( T . T ) [ 3 ] ) > cosh ( slr : : range_xy ) ) return false ;
if ( abs ( T . shift ) > ( slr : : range_z ) ) return false ;
2020-07-28 15:04:39 +00:00
if ( abs ( T . shift * stretch : : not_squared ( ) ) > sightranges [ geometry ] ) return false ;
2019-08-25 17:14:03 +00:00
if ( ! limited_generation ( c ) ) return false ;
}
2021-03-30 09:27:48 +00:00
# endif
2019-07-28 09:07:21 +00:00
else if ( vid . use_smart_range ) {
2023-01-04 22:26:46 +00:00
if ( cells_drawn > = min_cells_drawn & & ! in_smart_range ( T ) ) return false ;
2019-05-15 13:49:09 +00:00
if ( ! limited_generation ( c ) ) return false ;
2019-03-21 18:13:45 +00:00
}
else {
2020-07-27 16:49:04 +00:00
ld dist = hdist0 ( tC0 ( T . T ) ) ;
2019-05-26 16:04:02 +00:00
if ( dist > sightranges [ geometry ] + ( vid . sloppy_3d ? 0 : cgi . corner_bonus ) ) return false ;
2019-05-15 13:49:09 +00:00
if ( dist < = extra_generation_distance & & ! limited_generation ( c ) ) return false ;
2019-03-21 18:13:45 +00:00
}
2019-02-24 21:11:05 +00:00
return true ;
}
2021-05-12 18:15:40 +00:00
# if MAXMDIM >= 4
2024-06-16 16:12:52 +00:00
if ( hybrid : : drawing_underlying & & euclid & & hdist0 ( tC0 ( T ) ) > 6 ) return false ;
2021-05-12 18:15:40 +00:00
# endif
2019-02-08 09:04:01 +00:00
if ( just_gmatrix & & sphere ) return true ;
2018-11-10 13:26:49 +00:00
if ( ! do_draw ( c ) ) return false ;
2018-11-10 15:56:08 +00:00
if ( euclid & & pmodel = = mdSpiral ) {
2020-07-27 16:49:04 +00:00
hyperpoint h = tC0 ( T . T ) ;
2018-11-10 15:56:08 +00:00
cld z ( h [ 0 ] , h [ 1 ] ) ;
2019-08-09 22:58:50 +00:00
z = z * models : : spiral_multiplier ;
2018-11-10 15:56:08 +00:00
ld iz = imag ( z ) + 1.14279e-2 ; // make it never fall exactly on PI
if ( iz < - M_PI | | iz > = M_PI ) return false ;
}
2019-08-09 22:58:50 +00:00
if ( pmodel = = mdSpiral & & models : : ring_not_spiral ) {
2018-12-21 13:42:59 +00:00
cld z ;
2020-07-27 16:49:04 +00:00
shiftpoint H = tC0 ( T ) ;
2018-12-21 13:42:59 +00:00
hyperpoint ret ;
makeband ( H , ret , band_conformal ) ;
2019-08-09 22:58:50 +00:00
z = cld ( ret [ 0 ] , ret [ 1 ] ) * models : : spiral_multiplier ;
if ( imag ( z ) < - models : : spiral_cone_rad / 2 - 1e-5 | | imag ( z ) > = models : : spiral_cone_rad / 2 - 1e-5 ) return false ;
2018-12-21 13:42:59 +00:00
}
2018-11-10 13:26:49 +00:00
if ( cells_drawn > vid . cells_drawn_limit ) return false ;
2019-11-27 00:01:20 +00:00
bool usr = vid . use_smart_range | | quotient ;
2023-01-04 22:26:46 +00:00
if ( usr & & cells_drawn > = min_cells_drawn & & ! in_smart_range ( T ) & & ! ( WDIM = = 2 & & GDIM = = 3 & & hdist0 ( tC0 ( T ) ) < 2.5 ) ) return false ;
2019-05-15 13:49:09 +00:00
if ( vid . use_smart_range = = 2 & & ! limited_generation ( c ) ) return false ;
2018-11-10 13:26:49 +00:00
return true ;
}
2020-07-27 16:49:04 +00:00
EX int cone_side ( const shiftpoint H ) {
2018-12-21 13:42:59 +00:00
hyperpoint ret ;
if ( hyperbolic ) makeband ( H , ret , band_conformal ) ;
2020-07-27 16:49:04 +00:00
else ret = unshift ( H ) ;
2019-08-09 22:58:50 +00:00
cld z = cld ( ret [ 0 ] , ret [ 1 ] ) * models : : spiral_multiplier ;
2018-12-21 13:42:59 +00:00
auto zth = [ & ] ( cld z ) {
2020-04-16 22:53:58 +00:00
ld alpha = imag ( z ) * 360 / pconf . spiral_cone ;
2018-12-21 13:42:59 +00:00
ld r = real ( z ) ;
r = exp ( r ) ;
hyperpoint ret ;
ret [ 0 ] = - sin ( alpha ) * r ;
ret [ 1 ] = cos ( alpha ) * r ;
2020-04-16 22:53:58 +00:00
ret [ 2 ] = ( r - 1 ) * sqrt ( pow ( 360 / pconf . spiral_cone , 2 ) - 1 ) ;
2018-12-21 13:42:59 +00:00
2023-08-14 14:18:44 +00:00
ret = pconf . ball ( ) * ret ;
2018-12-21 13:42:59 +00:00
return ret ;
} ;
hyperpoint ret0 = zth ( z ) ;
hyperpoint ret1 = zth ( z + cld ( 1e-3 , 0 ) ) ;
hyperpoint ret2 = zth ( z + cld ( 0 , 1e-3 ) ) ;
return ( ret1 [ 1 ] - ret0 [ 1 ] ) * ( ret2 [ 0 ] - ret0 [ 0 ] ) < ( ret2 [ 1 ] - ret0 [ 1 ] ) * ( ret1 [ 0 ] - ret0 [ 0 ] ) ? 1 : - 1 ;
}
2019-08-24 16:14:38 +00:00
/** get the current orientation of the view */
EX transmatrix & get_view_orientation ( ) {
2022-12-08 18:38:06 +00:00
return gproduct ? NLP : View ;
2019-08-24 16:14:38 +00:00
}
2020-04-11 18:47:14 +00:00
EX hookset < bool ( const transmatrix & ) > hooks_rotate_view ;
EX hookset < bool ( const hyperpoint & ) > hooks_shift_view ;
2020-03-29 15:35:42 +00:00
2019-08-24 16:14:38 +00:00
/** rotate the view using the given rotation matrix */
EX void rotate_view ( transmatrix T ) {
2020-03-29 15:35:42 +00:00
if ( callhandlers ( false , hooks_rotate_view , T ) ) return ;
2024-06-29 08:48:57 +00:00
for ( auto pV : move_affected_matrices ( 3 ) ) ( * pV ) = T * ( * pV ) ;
2019-08-24 16:14:38 +00:00
}
2022-12-29 20:19:08 +00:00
EX shiftpoint lie_exp ( hyperpoint h1 ) {
shiftpoint sh = shiftless ( h1 ) ;
auto & h = sh . h ;
2022-05-06 00:36:16 +00:00
if ( nil ) {
h [ 3 ] = 1 ;
h [ 2 ] + = h [ 0 ] * h [ 1 ] / 2 ;
}
2022-05-17 07:37:32 +00:00
else if ( sol & & ! nih ) {
2022-05-06 00:36:16 +00:00
h [ 3 ] = 1 ;
2022-05-17 07:37:32 +00:00
if ( abs ( h [ 2 ] ) > 1e-6 ) {
h [ 0 ] * = ( exp ( - h [ 2 ] ) - 1 ) / - h [ 2 ] ;
h [ 1 ] * = ( exp ( + h [ 2 ] ) - 1 ) / h [ 2 ] ;
}
2022-05-06 00:36:16 +00:00
}
else if ( sol & & nih ) {
h [ 3 ] = 1 ;
2022-05-17 07:37:32 +00:00
if ( abs ( h [ 2 ] ) > 1e-6 ) {
ld z = h [ 2 ] * log ( 2 ) ;
h [ 0 ] * = ( exp ( - z ) - 1 ) / - z ;
z = h [ 2 ] * log ( 3 ) ;
h [ 1 ] * = ( exp ( + z ) - 1 ) / z ;
}
2022-05-06 00:36:16 +00:00
}
else if ( nih ) {
h [ 3 ] = 1 ;
2022-05-17 07:37:32 +00:00
if ( abs ( h [ 2 ] ) > 1e-6 ) {
ld z = h [ 2 ] * log ( 2 ) ;
h [ 0 ] * = ( exp ( + z ) - 1 ) / z ;
z = h [ 2 ] * log ( 3 ) ;
h [ 1 ] * = ( exp ( + z ) - 1 ) / z ;
}
2022-05-06 00:36:16 +00:00
}
2022-12-25 11:14:36 +00:00
else if ( sl2 ) {
h [ 3 ] = 0 ;
ld v = h [ 0 ] * h [ 0 ] + h [ 1 ] * h [ 1 ] - h [ 2 ] * h [ 2 ] ;
2022-12-29 20:19:08 +00:00
if ( v < 0 ) {
v = sqrt ( - v ) ;
h * = sin ( v ) / v ;
2022-12-25 11:14:36 +00:00
h [ 3 ] + = cos ( v ) ;
2022-12-29 20:19:08 +00:00
ld cycles = floor ( v / TAU + .5 ) ;
sh . shift + = TAU * cycles * ( h [ 2 ] > 0 ? 1 : - 1 ) ;
2022-12-25 11:14:36 +00:00
}
2022-12-29 20:19:08 +00:00
else if ( v > 0 ) {
v = sqrt ( v ) ;
h * = sinh ( v ) / v ;
2022-12-25 11:14:36 +00:00
h [ 3 ] + = cosh ( v ) ;
}
else h [ 3 ] + + ;
2022-12-29 20:19:08 +00:00
return sh ;
2022-12-25 11:14:36 +00:00
}
2022-05-06 00:50:30 +00:00
else {
/* not implemented -- approximate for now */
const int steps = 16 ;
h / = ( 1 < < steps ) ;
h [ 3 ] = 1 ;
normalize ( h ) ;
transmatrix T = eupush ( h ) ;
for ( int i = 0 ; i < 16 ; i + + ) T = T * T ;
h = tC0 ( T ) ;
2022-05-06 00:36:16 +00:00
}
2022-12-29 20:19:08 +00:00
return sh ;
2022-05-06 00:36:16 +00:00
}
2022-12-29 20:19:08 +00:00
/** Compute the Lie logarithm in SL(2,R), which corresponds to a geodesic in AdS; or a geodesic in de Sitter space.
2022-12-25 11:14:36 +00:00
* */
2023-04-14 23:18:47 +00:00
# if MAXMDIM >= 4
2022-12-25 11:14:36 +00:00
EX hyperpoint rel_log ( shiftpoint h , bool relativistic_length ) {
2022-10-13 22:56:48 +00:00
if ( sl2 ) {
optimize_shift ( h ) ;
2022-11-12 21:38:45 +00:00
ld cycles = floor ( h . shift / TAU + .5 ) ;
2022-10-13 22:56:48 +00:00
hyperpoint h1 = unshift ( h ) ;
ld choice = h1 [ 2 ] * h1 [ 2 ] - h1 [ 0 ] * h1 [ 0 ] - h1 [ 1 ] * h1 [ 1 ] ;
ld r , z ;
if ( choice > 0 ) {
2023-04-14 23:19:41 +00:00
r = sqrt ( choice ) ;
z = asin_clamp ( r ) ;
2022-10-13 22:56:48 +00:00
if ( h1 [ 3 ] < 0 ) z = M_PI - z ;
2022-11-12 21:38:45 +00:00
z + = cycles * TAU ;
2022-10-13 22:56:48 +00:00
}
2022-12-29 20:19:08 +00:00
else if ( cycles | | h1 [ 3 ] < - 1 ) {
/* impossible, or light-like */
r = 1 ; z = 0 ;
}
else if ( choice = = 0 ) {
2022-12-25 11:14:36 +00:00
if ( ! relativistic_length ) return h1 - C0 ;
2022-10-13 22:56:48 +00:00
/* impossible, or light-like */
r = 1 ; z = 0 ;
}
else {
r = sqrt ( - choice ) ;
z = asinh ( r ) ;
}
h1 = h1 * z / r ;
h1 [ 3 ] = 0 ;
return h1 ;
}
if ( hyperbolic & & GDIM = = 3 ) {
hyperpoint h1 = h . h ;
ld choice = h1 [ 3 ] * h1 [ 3 ] - h1 [ 0 ] * h1 [ 0 ] - h1 [ 1 ] * h1 [ 1 ] ;
ld r , z ;
if ( choice > 0 ) { r = sqrt ( choice ) ; z = asinh ( r ) ; }
else { r = sqrt ( - choice ) ; z = asin_clamp ( r ) ; if ( h1 [ 2 ] < 0 ) z = M_PI - z ; }
2022-12-25 11:14:36 +00:00
if ( ! relativistic_length ) r = sqrt ( h1 [ 3 ] * h1 [ 3 ] + h1 [ 0 ] * h1 [ 0 ] + h1 [ 1 ] * h1 [ 1 ] ) ;
2022-10-13 22:56:48 +00:00
h1 = h1 * z / r ; h1 [ 2 ] = h1 [ 3 ] ; h1 [ 3 ] = 0 ;
return h1 ;
}
throw hr_exception ( " rel_log in wrong geometry " ) ;
}
2023-04-14 23:18:47 +00:00
# endif
2022-10-13 22:56:48 +00:00
2022-12-25 11:14:36 +00:00
/** Is Lie movement available? Depends on map geometry, not ambient geometry. */
EX bool lie_movement_available ( ) {
if ( nonisotropic & & ! embedded_plane ) return true ;
if ( mhyperbolic & & bt : : in ( ) ) return true ;
return false ;
2023-10-17 04:02:54 +00:00
}
2022-12-25 11:14:36 +00:00
EX hyperpoint lie_log ( const shiftpoint h1 ) {
hyperpoint h = unshift ( h1 ) ;
2023-04-14 23:18:47 +00:00
if ( 0 ) ;
# if MAXMDIM >= 4
else if ( nil ) {
2022-05-17 07:40:33 +00:00
h [ 3 ] = 0 ;
2024-07-21 19:27:13 +00:00
h [ 2 ] - = nilv : : model_used * nilv : : sym_to_heis_bonus ( h ) ;
2022-05-17 07:40:33 +00:00
}
else if ( sol & & ! nih ) {
h [ 3 ] = 0 ;
2022-07-01 18:04:45 +00:00
if ( abs ( h [ 2 ] ) > 1e-6 ) {
2022-05-17 07:40:33 +00:00
h [ 0 ] * = - h [ 2 ] / ( exp ( - h [ 2 ] ) - 1 ) ;
h [ 1 ] * = h [ 2 ] / ( exp ( + h [ 2 ] ) - 1 ) ;
}
}
else if ( sol & & nih ) {
h [ 3 ] = 0 ;
2022-07-01 18:04:45 +00:00
if ( abs ( h [ 2 ] ) > 1e-6 ) {
2022-05-17 07:40:33 +00:00
ld z = h [ 2 ] * log ( 2 ) ;
h [ 0 ] * = - z / ( exp ( - z ) - 1 ) ;
z = h [ 2 ] * log ( 3 ) ;
h [ 1 ] * = z / ( exp ( + z ) - 1 ) ;
}
}
else if ( nih ) {
h [ 3 ] = 1 ;
2022-07-01 18:04:45 +00:00
if ( abs ( h [ 2 ] ) > 1e-6 ) {
2022-05-17 07:40:33 +00:00
ld z = h [ 2 ] * log ( 2 ) ;
h [ 0 ] * = z / ( exp ( + z ) - 1 ) ;
z = h [ 2 ] * log ( 3 ) ;
h [ 1 ] * = z / ( exp ( + z ) - 1 ) ;
}
}
2023-04-14 23:18:47 +00:00
else if ( sl2 ) {
return rel_log ( h1 , false ) ;
}
# endif
2022-05-17 07:40:33 +00:00
else if ( euclid ) {
h [ LDIM ] = 0 ;
}
else if ( hyperbolic ) {
h = deparabolic13 ( h ) ;
if ( abs ( h [ 0 ] ) > 1e-6 )
for ( int i = 1 ; i < LDIM ; i + + )
h [ i ] * = h [ 0 ] / ( exp ( h [ 0 ] ) - 1 ) ;
}
else {
/* not implemented */
}
return h ;
}
2022-12-25 11:14:36 +00:00
/** Like lie_log but includes orientation and level in hyperbolic space. May modify H */
EX hyperpoint lie_log_correct ( const shiftpoint H_orig , hyperpoint & H ) {
find_zlev ( H ) ;
if ( hyperbolic ) {
2023-08-14 09:08:37 +00:00
models : : scr_to_ori ( H ) ;
2022-12-25 11:14:36 +00:00
return lie_log ( shiftless ( H ) ) ;
}
return lie_log ( H_orig ) ;
}
2022-12-29 20:19:08 +00:00
/** Shift the view according to the given tangent vector. NOTE: known bug when // note: possible error when lie_exp includes a shift!*/
2023-01-08 01:36:06 +00:00
EX void shift_v_by_vector ( transmatrix & V , const hyperpoint H , eShiftMethod sm IS ( shift_method ( smaManualCamera ) ) ) {
2022-12-16 22:03:00 +00:00
switch ( sm ) {
case smProduct :
2023-01-08 01:36:06 +00:00
V = rgpushxto0 ( direct_exp ( lp_iapply ( H ) ) ) * V ;
return ;
2022-12-17 20:07:32 +00:00
case smIsotropic :
2023-01-08 01:36:06 +00:00
V = rgpushxto0 ( direct_exp ( H ) ) * V ;
return ;
2022-12-16 22:03:00 +00:00
case smEmbedded :
2023-01-08 01:36:06 +00:00
return shift_v_embedded ( V , rgpushxto0 ( direct_exp ( H ) ) ) ;
2022-12-16 22:03:00 +00:00
case smLie : {
2023-01-08 01:36:06 +00:00
transmatrix IV = view_inverse ( V ) ;
2022-12-16 22:03:00 +00:00
transmatrix view_shift = eupush ( tC0 ( IV ) ) ;
transmatrix rot = V * view_shift ;
2022-12-29 20:19:08 +00:00
hyperpoint tH = lie_exp ( inverse ( rot ) * H ) . h ;
2023-01-08 01:36:06 +00:00
V = rot * eupush ( tH ) * inverse ( view_shift ) ;
return ;
2022-12-16 22:03:00 +00:00
}
case smGeodesic :
2023-01-08 01:36:06 +00:00
V = iview_inverse ( nisot : : parallel_transport ( view_inverse ( V ) , - H ) ) ;
return ;
2022-12-16 22:03:00 +00:00
default :
throw hr_exception ( " unknown shift method (embedded not supported) " ) ;
2019-08-24 16:14:38 +00:00
}
}
2023-01-08 01:36:06 +00:00
EX transmatrix get_shift_view_of ( const hyperpoint H , transmatrix V , eShiftMethod sm IS ( shift_method ( smaManualCamera ) ) ) {
shift_v_by_vector ( V , H , sm ) ;
return V ;
}
2019-08-24 16:14:38 +00:00
/** shift the view according to the given tangent vector */
2022-12-17 10:47:10 +00:00
EX void shift_view ( hyperpoint H , eShiftMethod sm IS ( shift_method ( smaManualCamera ) ) ) {
2020-03-29 15:35:42 +00:00
if ( callhandlers ( false , hooks_shift_view , H ) ) return ;
2021-09-16 19:30:26 +00:00
static bool recursive = false ;
if ( ! recursive & & intra : : in ) {
2021-10-07 10:43:45 +00:00
dynamicval < bool > r ( recursive , true ) ;
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-10-07 10:43:45 +00:00
intra : : shift_view_portal ( H ) ;
2022-02-17 20:00:10 +00:00
# endif
2021-09-16 19:30:26 +00:00
return ;
}
2024-06-29 08:48:57 +00:00
for ( auto pV : move_affected_matrices ( 0 ) ) * pV = get_shift_view_of ( H , * pV , sm ) ;
2019-08-24 16:14:38 +00:00
}
2023-01-07 11:22:14 +00:00
/** works in embedded_plane (except embedded product where shift_view works, and euc_in_sl2) */
2023-01-08 01:36:06 +00:00
EX void shift_v_embedded ( transmatrix & V , const transmatrix T ) {
2022-12-17 20:29:15 +00:00
transmatrix IV = view_inverse ( V ) ;
2023-01-26 23:27:10 +00:00
transmatrix rot = V * cgi . emb - > map_relative_push ( IV * C0 ) ;
2022-12-17 20:29:15 +00:00
transmatrix V1 = T * V ;
transmatrix IV1 = view_inverse ( V1 ) ;
2023-01-26 23:27:10 +00:00
transmatrix rot1 = V1 * cgi . emb - > map_relative_push ( IV1 * C0 ) ;
2023-01-08 01:36:06 +00:00
V = rot * inverse ( rot1 ) * V1 ;
2019-11-23 18:05:24 +00:00
}
2023-01-08 01:36:06 +00:00
EX void shift_v_by_matrix ( transmatrix & V , const transmatrix T , eShiftMethod sm ) {
2022-12-16 22:03:00 +00:00
switch ( sm ) {
case smEmbedded :
2023-01-08 01:36:06 +00:00
return shift_v_embedded ( V , T ) ;
2022-12-17 20:07:32 +00:00
case smIsotropic :
2022-12-16 22:03:00 +00:00
case smProduct :
2023-01-08 01:36:06 +00:00
V = T * V ;
2022-12-16 22:03:00 +00:00
return ;
default :
throw hr_exception ( " unsupported shift method in shift_view_by_matrix " ) ;
}
}
2022-12-17 10:47:10 +00:00
EX void shift_view_to ( shiftpoint H , eShiftMethod sm IS ( shift_method ( smaManualCamera ) ) ) {
2024-06-29 08:48:57 +00:00
for ( auto pV : move_affected_matrices ( 0 ) ) shift_v_to ( * pV , H , sm ) ;
2023-01-08 01:36:06 +00:00
}
EX void shift_v_to ( transmatrix & V , shiftpoint H , eShiftMethod sm IS ( shift_method ( smaManualCamera ) ) ) {
2022-12-16 22:03:00 +00:00
switch ( sm ) {
2022-12-17 20:29:25 +00:00
case smIsotropic :
2022-12-16 22:03:00 +00:00
case smEmbedded :
case smProduct :
2023-01-08 01:36:06 +00:00
return shift_v_by_matrix ( V , gpushxto0 ( unshift ( H ) ) , sm ) ;
2022-12-16 22:03:00 +00:00
case smLie :
2023-01-08 01:36:06 +00:00
return shift_v_by_vector ( V , - lie_log ( H ) , sm ) ;
2022-12-16 22:03:00 +00:00
return ;
case smGeodesic :
2023-01-08 01:36:06 +00:00
return shift_v_by_vector ( V , - inverse_exp ( H ) , sm ) ;
2022-12-16 22:03:00 +00:00
default :
throw hr_exception ( " unsupported shift method in shift_view_to " ) ;
}
2019-08-24 16:14:38 +00:00
}
2022-12-17 10:47:10 +00:00
EX void shift_view_towards ( shiftpoint H , ld l , eShiftMethod sm IS ( shift_method ( smaManualCamera ) ) ) {
2024-06-29 08:48:57 +00:00
for ( auto pV : move_affected_matrices ( 0 ) ) shift_v_towards ( * pV , H , l , sm ) ;
2023-01-08 01:36:06 +00:00
}
EX void shift_v_towards ( transmatrix & V , shiftpoint H , ld l , eShiftMethod sm IS ( shift_method ( smaManualCamera ) ) ) {
2022-12-16 22:03:00 +00:00
switch ( sm ) {
2022-12-17 20:07:32 +00:00
case smIsotropic :
2022-12-16 22:03:00 +00:00
case smEmbedded :
2023-01-08 01:36:06 +00:00
return shift_v_by_matrix ( V , rspintox ( unshift ( H ) ) * xpush ( - l ) * spintox ( unshift ( H ) ) , sm ) ;
2022-12-16 22:03:00 +00:00
case smLie :
2023-01-08 01:36:06 +00:00
return shift_v_by_vector ( V , tangent_length ( lie_log ( H ) , - l ) , sm ) ;
2022-12-16 22:03:00 +00:00
case smGeodesic :
case smProduct : {
hyperpoint ie = inverse_exp ( H , pNORMAL | pfNO_DISTANCE ) ;
if ( gproduct ) ie = lp_apply ( ie ) ;
2023-01-08 01:36:06 +00:00
return shift_v_by_vector ( V , tangent_length ( ie , - l ) , sm ) ;
2022-12-16 22:03:00 +00:00
}
default :
throw hr_exception ( " unsupported shift method in shift_view_towards " ) ;
2019-08-24 16:14:38 +00:00
}
}
2020-09-11 09:11:30 +00:00
EX void set_view ( hyperpoint camera , hyperpoint forward , hyperpoint upward ) {
2021-08-08 16:29:35 +00:00
if ( GDIM = = 2 ) {
2021-04-06 23:11:12 +00:00
View = gpushxto0 ( camera ) ;
2022-11-12 21:38:45 +00:00
View = spin90 ( ) * spintox ( View * upward ) * View ;
2021-04-06 23:11:12 +00:00
return ;
}
2020-09-11 09:11:30 +00:00
transmatrix V = gpushxto0 ( camera ) ;
forward = V * forward ;
upward = V * upward ;
2023-02-04 22:07:43 +00:00
if ( pmodel = = mdGeodesic | | hyperbolic | | sphere | | euclid | | mproduct ) {
2020-09-11 09:11:30 +00:00
forward = inverse_exp ( shiftless ( forward ) ) ;
}
else {
// apply_nil_rotation(forward);
forward - = C0 ;
}
if ( hypot_d ( 3 , forward ) = = 0 ) forward [ 0 ] = 1 ;
forward / = hypot_d ( 3 , forward ) ;
2023-02-04 22:07:43 +00:00
if ( pmodel = = mdGeodesic | | hyperbolic | | sphere | | euclid | | mproduct )
2020-09-11 09:11:30 +00:00
upward = inverse_exp ( shiftless ( upward ) ) ;
else {
// apply_nil_rotation(upward);
upward - = C0 ;
}
upward - = ( forward | upward ) * forward ;
if ( hypot_d ( 3 , upward ) = = 0 ) return ;
upward / = hypot_d ( 3 , upward ) ;
hyperpoint rightward = ( forward ^ upward ) ;
transmatrix rotator = Id ;
rotator [ 2 ] = forward ;
rotator [ 1 ] = - upward ;
rotator [ 0 ] = - rightward ;
if ( det ( rotator ) < 0 ) rotator [ 0 ] = - rotator [ 0 ] ;
2021-04-23 18:11:44 +00:00
View = iso_inverse ( rgpushxto0 ( camera ) ) ;
2022-12-08 18:38:06 +00:00
if ( gproduct )
2021-04-23 18:11:44 +00:00
NLP = rotator ;
else
View = rotator * View ;
2020-09-11 09:11:30 +00:00
}
2018-06-10 23:58:31 +00:00
}