2019-08-10 11:43:24 +00:00
// Hyperbolic Rogue -- models of hyperbolic geometry
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
/** \file models.cpp
* \ brief models of hyperbolic geometry : their properties , projection menu
*
* The actual models are implemented in hypgraph . cpp . Also shaders . cpp ,
* drawing . cpp , and basegraph . cpp are important .
*/
2019-09-05 07:15:40 +00:00
# include "hyper.h"
2019-08-09 22:58:50 +00:00
namespace hr {
EX namespace polygonal {
# if ISMOBWEB
typedef double precise ;
# else
typedef long double precise ;
# endif
# if HDR
static const int MSI = 120 ;
# endif
typedef long double xld ;
typedef complex < xld > cxld ;
EX int SI = 4 ;
EX ld STAR = 0 ;
EX int deg = ISMOBWEB ? 2 : 20 ;
precise matrix [ MSI ] [ MSI ] ;
precise ans [ MSI ] ;
cxld coef [ MSI ] ;
EX ld coefr [ MSI ] , coefi [ MSI ] ;
EX int maxcoef , coefid ;
EX void solve ( ) {
if ( pmodel = = mdPolynomial ) {
for ( int i = 0 ; i < MSI ; i + + ) coef [ i ] = cxld ( coefr [ i ] , coefi [ i ] ) ;
return ;
}
if ( pmodel ! = mdPolygonal ) return ;
if ( SI < 3 ) SI = 3 ;
for ( int i = 0 ; i < MSI ; i + + ) ans [ i ] = cos ( M_PI / SI ) ;
for ( int i = 0 ; i < MSI ; i + + )
for ( int j = 0 ; j < MSI ; j + + ) {
precise i0 = ( i + 0. ) / ( MSI - 1 ) ;
// i0 *= i0;
// i0 = 1 - i0;
i0 * = M_PI ;
matrix [ i ] [ j ] =
cos ( i0 * ( j + 1. / SI ) ) * ( STAR > 0 ? ( 1 + STAR ) : 1 )
- sin ( i0 * ( j + 1. / SI ) ) * ( STAR > 0 ? STAR : STAR / ( 1 + STAR ) ) ;
}
for ( int i = 0 ; i < MSI ; i + + ) {
precise dby = matrix [ i ] [ i ] ;
for ( int k = 0 ; k < MSI ; k + + ) matrix [ i ] [ k ] / = dby ;
ans [ i ] / = dby ;
for ( int j = i + 1 ; j < MSI ; j + + ) {
precise sub = matrix [ j ] [ i ] ;
ans [ j ] - = ans [ i ] * sub ;
for ( int k = 0 ; k < MSI ; k + + )
matrix [ j ] [ k ] - = sub * matrix [ i ] [ k ] ;
}
}
for ( int i = MSI - 1 ; i > = 0 ; i - - ) {
for ( int j = 0 ; j < i ; j + + ) {
precise sub = matrix [ j ] [ i ] ;
ans [ j ] - = ans [ i ] * sub ;
for ( int k = 0 ; k < MSI ; k + + )
matrix [ j ] [ k ] - = sub * matrix [ i ] [ k ] ;
}
}
}
EX pair < ld , ld > compute ( ld x , ld y , int prec ) {
if ( x * x + y * y > 1 ) {
xld r = hypot ( x , y ) ;
x / = r ;
y / = r ;
}
if ( pmodel = = mdPolynomial ) {
cxld z ( x , y ) ;
cxld res ( 0 , 0 ) ;
for ( int i = maxcoef ; i > = 0 ; i - - ) { res + = coef [ i ] ; if ( i ) res * = z ; }
return make_pair ( real ( res ) , imag ( res ) ) ;
}
cxld z ( x , y ) ;
cxld res ( 0 , 0 ) ;
cxld zp = 1 ; for ( int i = 0 ; i < SI ; i + + ) zp * = z ;
for ( int i = prec ; i > 0 ; i - - ) {
res + = ans [ i ] ;
res * = zp ;
}
res + = ans [ 0 ] ; res * = z ;
return make_pair ( real ( res ) , imag ( res ) ) ;
}
2019-09-06 06:17:02 +00:00
EX pair < ld , ld > compute ( ld x , ld y ) { return compute ( x , y , deg ) ; }
2019-08-09 22:58:50 +00:00
EX }
2019-08-10 00:16:48 +00:00
# if HDR
inline bool mdAzimuthalEqui ( ) { return among ( pmodel , mdEquidistant , mdEquiarea , mdEquivolume ) ; }
2019-08-14 13:25:00 +00:00
inline bool mdBandAny ( ) { return mdinf [ pmodel ] . flags & mf : : pseudoband ; }
2019-08-14 13:28:55 +00:00
inline bool mdPseudocylindrical ( ) { return mdBandAny ( ) & & ! ( mdinf [ pmodel ] . flags & mf : : cylindrical ) ; }
2019-08-10 00:16:48 +00:00
# endif
2019-08-09 22:58:50 +00:00
EX namespace models {
EX ld rotation = 0 ;
EX ld rotation_xz = 90 ;
EX ld rotation_xy2 = 90 ;
EX int do_rotate = 1 ;
EX ld ocos , osin , ocos_yz , osin_yz ;
2020-12-31 01:53:25 +00:00
EX ld cos_ball , sin_ball ;
2019-08-09 22:58:50 +00:00
EX bool model_straight , model_straight_yz ;
# if HDR
// screen coordinates to logical coordinates: apply_orientation(x,y)
// logical coordinates back to screen coordinates: apply_orientation(y,x)
template < class A >
void apply_orientation ( A & x , A & y ) { if ( ! model_straight ) tie ( x , y ) = make_pair ( x * ocos + y * osin , y * ocos - x * osin ) ; }
template < class A >
void apply_orientation_yz ( A & x , A & y ) { if ( ! model_straight_yz ) tie ( x , y ) = make_pair ( x * ocos_yz + y * osin_yz , y * ocos_yz - x * osin_yz ) ; }
template < class A >
void apply_ball ( A & x , A & y ) { tie ( x , y ) = make_pair ( x * cos_ball + y * sin_ball , y * cos_ball - x * sin_ball ) ; }
# endif
EX transmatrix rotmatrix ( ) {
2019-08-17 23:31:37 +00:00
if ( GDIM = = 2 | | prod ) return spin ( rotation * degree ) ;
2019-08-09 22:58:50 +00:00
return spin ( rotation_xy2 * degree ) * cspin ( 0 , 2 , - rotation_xz * degree ) * spin ( rotation * degree ) ;
}
int spiral_id = 7 ;
2019-09-06 06:17:02 +00:00
EX cld spiral_multiplier ;
EX ld spiral_cone_rad ;
EX bool ring_not_spiral ;
2019-11-09 12:14:42 +00:00
2019-11-28 19:16:17 +00:00
/** the matrix to rotate the Euclidean view from the standard coordinates to the screen coordinates */
EX transmatrix euclidean_spin ;
2019-08-09 22:58:50 +00:00
EX void configure ( ) {
2020-04-16 22:53:58 +00:00
ld ball = - pconf . ballangle * degree ;
2019-08-09 22:58:50 +00:00
cos_ball = cos ( ball ) , sin_ball = sin ( ball ) ;
2020-04-16 22:53:58 +00:00
ocos = cos ( pconf . model_orientation * degree ) ;
osin = sin ( pconf . model_orientation * degree ) ;
ocos_yz = cos ( pconf . model_orientation_yz * degree ) ;
osin_yz = sin ( pconf . model_orientation_yz * degree ) ;
2019-08-09 22:58:50 +00:00
model_straight = ( ocos > 1 - 1e-9 ) ;
2019-08-15 13:05:43 +00:00
model_straight_yz = GDIM = = 2 | | ( ocos_yz > 1 - 1e-9 ) ;
2019-08-09 22:58:50 +00:00
if ( history : : on ) history : : apply ( ) ;
if ( ! euclid ) {
2020-04-16 22:53:58 +00:00
ld b = pconf . spiral_angle * degree ;
2019-08-09 22:58:50 +00:00
ld cos_spiral = cos ( b ) ;
ld sin_spiral = sin ( b ) ;
2020-04-16 22:53:58 +00:00
spiral_cone_rad = pconf . spiral_cone * degree ;
2019-08-09 22:58:50 +00:00
ring_not_spiral = abs ( cos_spiral ) < 1e-3 ;
ld mul = 1 ;
2020-04-16 22:53:58 +00:00
if ( sphere ) mul = .5 * pconf . sphere_spiral_multiplier ;
else if ( ring_not_spiral ) mul = pconf . right_spiral_multiplier ;
else mul = pconf . any_spiral_multiplier * cos_spiral ;
2019-08-09 22:58:50 +00:00
spiral_multiplier = cld ( cos_spiral , sin_spiral ) * cld ( spiral_cone_rad * mul / 2. , 0 ) ;
}
if ( euclid ) {
2020-09-16 03:57:05 +00:00
euclidean_spin = pispin * iso_inverse ( cview ( ) . T * master_relative ( centerover , true ) ) ;
2019-11-28 19:16:17 +00:00
euclidean_spin = gpushxto0 ( euclidean_spin * C0 ) * euclidean_spin ;
2020-04-16 22:53:58 +00:00
hyperpoint h = inverse ( euclidean_spin ) * ( C0 + ( euc : : eumove ( gp : : loc { 1 , 0 } ) * C0 - C0 ) * vpconf . spiral_x + ( euc : : eumove ( gp : : loc { 0 , 1 } ) * C0 - C0 ) * vpconf . spiral_y ) ;
2019-08-09 22:58:50 +00:00
spiral_multiplier = cld ( 0 , 2 * M_PI ) / cld ( h [ 0 ] , h [ 1 ] ) ;
}
2019-11-13 23:26:50 +00:00
if ( centerover & & ! history : : on )
2020-01-15 17:19:59 +00:00
if ( isize ( history : : path_for_lineanimation ) = = 0 | | ( ( quotient | | arb : : in ( ) ) & & history : : path_for_lineanimation . back ( ) ! = centerover ) ) {
2019-11-13 23:26:50 +00:00
history : : path_for_lineanimation . push_back ( centerover ) ;
2019-08-09 22:58:50 +00:00
}
}
EX bool model_available ( eModel pm ) {
2019-11-09 12:14:42 +00:00
if ( prod ) {
if ( pm = = mdPerspective ) return true ;
if ( among ( pm , mdBall , mdHemisphere ) ) return false ;
return PIU ( model_available ( pm ) ) ;
}
2019-08-27 19:43:53 +00:00
if ( sl2 ) return pm = = mdGeodesic ;
2019-11-09 11:49:00 +00:00
if ( nonisotropic ) return among ( pm , mdDisk , mdPerspective , mdHorocyclic , mdGeodesic , mdEquidistant , mdFisheye ) ;
2019-08-09 22:58:50 +00:00
if ( pm = = mdGeodesic & & ! sol ) return false ;
if ( sphere & & ( pm = = mdHalfplane | | pm = = mdBall ) )
return false ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 2 & & pm = = mdPerspective ) return false ;
if ( GDIM = = 2 & & pm = = mdEquivolume ) return false ;
if ( GDIM = = 3 & & among ( pm , mdBall , mdHyperboloid , mdFormula , mdPolygonal , mdRotatedHyperboles , mdSpiral , mdHemisphere ) ) return false ;
2019-08-09 22:58:50 +00:00
if ( pm = = mdCentralInversion & & ! euclid ) return false ;
2020-10-08 16:22:28 +00:00
if ( pm = = mdPoorMan ) return hyperbolic ;
if ( pm = = mdRetroHammer ) return hyperbolic ;
2019-08-09 22:58:50 +00:00
return true ;
}
2020-04-16 22:53:58 +00:00
EX bool has_orientation ( eModel m ) {
2020-09-11 09:18:25 +00:00
if ( m = = mdHorocyclic )
return hyperbolic ;
2020-11-01 20:20:54 +00:00
if ( ( m = = mdPerspective | | m = = mdGeodesic ) & & panini_alpha ) return true ;
2019-08-09 22:58:50 +00:00
return
2020-09-15 17:17:07 +00:00
among ( m , mdHalfplane , mdPolynomial , mdPolygonal , mdTwoPoint , mdJoukowsky , mdJoukowskyInverted , mdSpiral , mdSimulatedPerspective , mdTwoHybrid , mdHorocyclic , mdAxial , mdAntiAxial , mdQuadrant ,
mdWerner , mdAitoff , mdHammer , mdLoximuthal , mdWinkelTripel ) | | mdBandAny ( ) ;
}
/** @brief returns the broken coordinate, or zero */
EX int get_broken_coord ( eModel m ) {
if ( m = = mdWerner ) return 1 ;
if ( sphere ) return ( mdinf [ m ] . flags & mf : : broken ) ? 2 : 0 ;
return 0 ;
2019-08-09 22:58:50 +00:00
}
2020-04-16 22:53:58 +00:00
EX bool is_perspective ( eModel m ) {
2020-04-16 12:36:45 +00:00
return among ( m , mdPerspective , mdGeodesic ) ;
}
2020-04-16 22:53:58 +00:00
EX bool is_3d ( const projection_configuration & p ) {
2020-04-16 12:36:45 +00:00
if ( GDIM = = 3 ) return true ;
2020-04-16 22:53:58 +00:00
return among ( p . model , mdBall , mdHyperboloid , mdHemisphere ) | | ( p . model = = mdSpiral & & p . spiral_cone ! = 360 ) ;
2020-04-16 12:36:45 +00:00
}
2020-04-16 22:53:58 +00:00
EX bool has_transition ( eModel m ) {
2020-08-20 14:02:34 +00:00
return among ( m , mdJoukowsky , mdJoukowskyInverted , mdBand , mdAxial ) & & GDIM = = 2 ;
2019-08-09 22:58:50 +00:00
}
2020-04-16 22:53:58 +00:00
EX bool product_model ( eModel m ) {
2019-11-09 12:14:42 +00:00
if ( ! prod ) return false ;
2020-04-16 22:53:58 +00:00
if ( among ( m , mdPerspective , mdHyperboloid , mdEquidistant ) ) return false ;
2019-11-09 12:14:42 +00:00
return true ;
}
2019-08-09 22:58:50 +00:00
int editpos = 0 ;
EX string get_model_name ( eModel m ) {
2019-10-05 10:34:14 +00:00
if ( m = = mdDisk & & GDIM = = 3 & & ( hyperbolic | | nonisotropic ) ) return XLAT ( " ball model/Gans " ) ;
2019-08-19 12:39:45 +00:00
if ( m = = mdPerspective & & prod ) return XLAT ( " native perspective " ) ;
2019-11-09 12:14:42 +00:00
if ( prod ) return PIU ( get_model_name ( m ) ) ;
2019-08-09 22:58:50 +00:00
if ( nonisotropic ) {
2019-11-09 11:50:38 +00:00
if ( m = = mdHorocyclic & & ! sol ) return XLAT ( " simple model: projection " ) ;
2019-08-09 22:58:50 +00:00
if ( m = = mdPerspective ) return XLAT ( " simple model: perspective " ) ;
if ( m = = mdGeodesic ) return XLAT ( " native perspective " ) ;
2019-11-09 11:49:00 +00:00
if ( among ( m , mdEquidistant , mdFisheye , mdHorocyclic ) ) return XLAT ( mdinf [ m ] . name_hyperbolic ) ;
2019-08-09 22:58:50 +00:00
}
2019-08-15 13:05:43 +00:00
if ( m = = mdDisk & & GDIM = = 3 ) return XLAT ( " perspective in 4D " ) ;
if ( m = = mdHalfplane & & GDIM = = 3 & & hyperbolic ) return XLAT ( " half-space " ) ;
2019-08-09 22:58:50 +00:00
if ( sphere )
return XLAT ( mdinf [ m ] . name_spherical ) ;
if ( euclid )
return XLAT ( mdinf [ m ] . name_euclidean ) ;
if ( hyperbolic )
return XLAT ( mdinf [ m ] . name_hyperbolic ) ;
return " ? " ;
}
2019-11-27 16:51:26 +00:00
vector < gp : : loc > torus_zeros ;
2019-08-09 22:58:50 +00:00
void match_torus_period ( ) {
torus_zeros . clear ( ) ;
for ( int y = 0 ; y < = 200 ; y + + )
for ( int x = - 200 ; x < = 200 ; x + + ) {
if ( y = = 0 & & x < = 0 ) continue ;
2019-12-08 10:25:14 +00:00
transmatrix dummy = Id ;
euc : : coord v ( x , y , 0 ) ;
bool mirr = false ;
auto t = euc : : eutester ;
euc : : eu . canonicalize ( v , t , dummy , mirr ) ;
if ( v = = euc : : euzero & & t = = euc : : eutester )
2019-08-09 22:58:50 +00:00
torus_zeros . emplace_back ( x , y ) ;
}
2019-11-27 16:51:26 +00:00
sort ( torus_zeros . begin ( ) , torus_zeros . end ( ) , [ ] ( const gp : : loc p1 , const gp : : loc p2 ) {
2019-12-08 09:59:09 +00:00
ld d1 = hdist0 ( tC0 ( euc : : eumove ( p1 ) ) ) ;
ld d2 = hdist0 ( tC0 ( euc : : eumove ( p2 ) ) ) ;
2019-08-09 22:58:50 +00:00
if ( d1 < d2 - 1e-6 ) return true ;
if ( d1 > d2 + 1e-6 ) return false ;
return p1 < p2 ;
} ) ;
if ( spiral_id > isize ( torus_zeros ) ) spiral_id = 0 ;
dialog : : editNumber ( spiral_id , 0 , isize ( torus_zeros ) - 1 , 1 , 10 , XLAT ( " match the period of the torus " ) , " " ) ;
dialog : : reaction = [ ] ( ) {
2019-11-30 17:47:43 +00:00
auto & co = torus_zeros [ spiral_id ] ;
2020-04-16 22:53:58 +00:00
vpconf . spiral_x = co . first ;
vpconf . spiral_y = co . second ;
2019-08-09 22:58:50 +00:00
} ;
dialog : : bound_low ( 0 ) ;
dialog : : bound_up ( isize ( torus_zeros ) - 1 ) ;
}
EX void edit_formula ( ) {
2020-04-16 22:53:58 +00:00
if ( vpconf . model ! = mdFormula ) vpconf . basic_model = vpconf . model ;
dialog : : edit_string ( vpconf . formula , " formula " ,
2019-08-09 22:58:50 +00:00
XLAT (
" This lets you specify the projection as a formula f. "
" The formula has access to the value 'z', which is a complex number corresponding to the (x,y) coordinates in the currently selected model; "
" the point z is mapped to f(z). You can also use the underlying coordinates ux, uy, uz. "
2021-02-01 12:42:12 +00:00
)
2019-08-09 22:58:50 +00:00
) ;
# if CAP_QUEUE && CAP_CURVE
dialog : : extra_options = [ ] ( ) {
2021-02-01 12:42:12 +00:00
dialog : : parser_help ( ) ;
2019-08-09 22:58:50 +00:00
initquickqueue ( ) ;
2019-10-21 20:34:20 +00:00
queuereset ( mdPixel , PPR : : LINE ) ;
2019-08-09 22:58:50 +00:00
for ( int a = - 1 ; a < = 1 ; a + + ) {
curvepoint ( point2 ( - M_PI / 2 * current_display - > radius , a * current_display - > radius ) ) ;
curvepoint ( point2 ( + M_PI / 2 * current_display - > radius , a * current_display - > radius ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , forecolor , 0 , PPR : : LINE ) ;
2019-08-09 22:58:50 +00:00
curvepoint ( point2 ( a * current_display - > radius , - M_PI / 2 * current_display - > radius ) ) ;
curvepoint ( point2 ( a * current_display - > radius , + M_PI / 2 * current_display - > radius ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( shiftless ( Id ) , forecolor , 0 , PPR : : LINE ) ;
2019-08-09 22:58:50 +00:00
}
2020-04-16 22:53:58 +00:00
queuereset ( vpconf . model , PPR : : LINE ) ;
2019-08-09 22:58:50 +00:00
quickqueue ( ) ;
} ;
# endif
dialog : : reaction_final = [ ] ( ) {
2020-04-16 22:53:58 +00:00
vpconf . model = mdFormula ;
2019-08-09 22:58:50 +00:00
} ;
}
EX void edit_rotation ( ld & which ) {
dialog : : editNumber ( which , 0 , 360 , 90 , 0 , XLAT ( " rotation " ) ,
" This controls the automatic rotation of the world. "
" It affects the line animation in the history mode, and "
" lands which have a special direction. Note that if finding this special direction is a part of the puzzle, "
" it works only in the cheat mode. " ) ;
dialog : : dialogflags | = sm : : CENTER ;
dialog : : extra_options = [ ] ( ) {
dialog : : addBreak ( 100 ) ;
dialog : : addBoolItem_choice ( " line animation only " , models : : do_rotate , 0 , ' N ' ) ;
dialog : : addBoolItem_choice ( " gravity lands " , models : : do_rotate , 1 , ' G ' ) ;
dialog : : addBoolItem_choice ( " all directional lands " , models : : do_rotate , 2 , ' D ' ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2019-08-09 22:58:50 +00:00
dialog : : addBreak ( 100 ) ;
2019-08-10 20:46:45 +00:00
dialog : : addSelItem ( XLAT ( " XY plane " ) , fts ( models : : rotation ) + " ° " , ' A ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ ] { popScreen ( ) ; edit_rotation ( models : : rotation ) ; } ) ;
2019-08-10 20:46:45 +00:00
dialog : : addSelItem ( XLAT ( " XZ plane " ) , fts ( models : : rotation_xz ) + " ° " , ' B ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ ] { popScreen ( ) ; edit_rotation ( models : : rotation_xz ) ; } ) ;
2019-08-10 20:46:45 +00:00
dialog : : addSelItem ( XLAT ( " XY plane #2 " ) , fts ( models : : rotation_xy2 ) + " ° " , ' C ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ ] { popScreen ( ) ; edit_rotation ( models : : rotation_xy2 ) ; } ) ;
}
} ;
}
2019-08-14 06:32:05 +00:00
EX void model_list ( ) {
2019-08-09 22:58:50 +00:00
cmode = sm : : SIDE | sm : : MAYDARK | sm : : CENTER ;
gamescreen ( 0 ) ;
dialog : : init ( XLAT ( " models & projections " ) ) ;
2020-07-03 12:42:33 +00:00
# if CAP_RUG
2020-04-17 00:21:44 +00:00
USING_NATIVE_GEOMETRY_IN_RUG ;
2020-07-03 12:42:33 +00:00
# endif
2019-08-14 06:32:05 +00:00
2019-08-09 22:58:50 +00:00
for ( int i = 0 ; i < mdGUARD ; i + + ) {
eModel m = eModel ( i ) ;
if ( m = = mdFormula & & ISMOBILE ) continue ;
if ( model_available ( m ) ) {
2020-04-17 00:21:44 +00:00
dialog : : addBoolItem ( get_model_name ( m ) , vpconf . model = = m , ( i < 26 ? ' a ' + i : ' A ' + i - 26 ) ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ m ] ( ) {
if ( m = = mdFormula ) {
edit_formula ( ) ;
return ;
}
2020-04-17 00:21:44 +00:00
vpconf . model = m ;
2019-08-09 22:58:50 +00:00
polygonal : : solve ( ) ;
2020-04-16 22:53:58 +00:00
vpconf . alpha = 1 ; vpconf . scale = 1 ;
2019-08-09 22:58:50 +00:00
if ( pmodel = = mdBand & & sphere )
2020-04-16 22:53:58 +00:00
vpconf . scale = .3 ;
2019-08-09 22:58:50 +00:00
if ( pmodel = = mdDisk & & sphere )
2020-04-16 22:53:58 +00:00
vpconf . scale = .4 ;
2019-08-14 18:31:07 +00:00
popScreen ( ) ;
2019-08-09 22:58:50 +00:00
} ) ;
}
}
2019-08-14 06:32:05 +00:00
dialog : : display ( ) ;
}
2019-08-14 07:15:57 +00:00
2021-02-01 11:50:02 +00:00
void stretch_extra ( ) {
dialog : : addBreak ( 100 ) ;
if ( sphere & & pmodel = = mdBandEquiarea ) {
dialog : : addBoolItem ( " Gall-Peters " , vpconf . stretch = = 2 , ' O ' ) ;
dialog : : add_action ( [ ] { vpconf . stretch = 2 ; dialog : : ne . s = " 2 " ; } ) ;
}
if ( pmodel = = mdBandEquiarea ) {
// y = K * sin(phi)
// cos(phi) * cos(phi) = 1/K
if ( sphere & & vpconf . stretch > = 1 ) {
ld phi = acos ( sqrt ( 1 / vpconf . stretch ) ) ;
dialog : : addInfo ( XLAT ( " The current value makes the map conformal at the latitude of %1 (%2°). " , fts ( phi ) , fts ( phi / degree ) ) ) ;
2019-08-14 07:15:57 +00:00
}
2021-02-01 11:50:02 +00:00
else if ( hyperbolic & & abs ( vpconf . stretch ) < = 1 & & abs ( vpconf . stretch ) > = 1e-9 ) {
ld phi = acosh ( abs ( sqrt ( 1 / vpconf . stretch ) ) ) ;
dialog : : addInfo ( XLAT ( " The current value makes the map conformal %1 units from the main line. " , fts ( phi ) ) ) ;
2019-08-14 07:15:57 +00:00
}
2021-02-01 11:50:02 +00:00
else dialog : : addInfo ( " " ) ;
}
2019-08-14 07:15:57 +00:00
}
2020-12-31 01:53:25 +00:00
bool set_vr_settings = true ;
2019-08-14 07:15:57 +00:00
2019-08-14 06:32:05 +00:00
EX void model_menu ( ) {
cmode = sm : : SIDE | sm : : MAYDARK | sm : : CENTER ;
gamescreen ( 0 ) ;
2020-07-03 12:42:33 +00:00
# if CAP_RUG
2020-04-16 22:53:58 +00:00
USING_NATIVE_GEOMETRY_IN_RUG ;
2020-07-03 12:42:33 +00:00
# endif
2019-08-14 06:32:05 +00:00
dialog : : init ( XLAT ( " models & projections " ) ) ;
2020-04-17 00:29:03 +00:00
auto vpmodel = vpconf . model ;
dialog : : addSelItem ( XLAT ( " projection type " ) , get_model_name ( vpmodel ) , ' m ' ) ;
2019-08-14 06:32:05 +00:00
dialog : : add_action_push ( model_list ) ;
2019-08-09 22:58:50 +00:00
2019-08-27 19:42:59 +00:00
if ( nonisotropic & & ! sl2 )
2019-08-09 22:58:50 +00:00
dialog : : addBoolItem_action ( XLAT ( " geodesic movement in Sol/Nil " ) , nisot : : geodesic_movement , ' G ' ) ;
dialog : : addBoolItem ( XLAT ( " rotation " ) , do_rotate = = 2 , ' r ' ) ;
if ( do_rotate = = 0 ) dialog : : lastItem ( ) . value = XLAT ( " NEVER " ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 2 )
2019-08-10 20:46:45 +00:00
dialog : : lastItem ( ) . value + = " " + its ( rotation ) + " ° " ;
2019-08-09 22:58:50 +00:00
else
2019-08-10 20:46:45 +00:00
dialog : : lastItem ( ) . value + = " " + its ( rotation ) + " ° " + its ( rotation_xz ) + " ° " + its ( rotation_xy2 ) + " ° " ;
2020-04-16 22:53:58 +00:00
dialog : : add_action ( [ ] { edit_rotation ( rotation ) ; } ) ;
2019-08-09 22:58:50 +00:00
2020-12-31 01:53:25 +00:00
bool vr_settings = vrhr : : active ( ) & & set_vr_settings ;
if ( vrhr : : active ( ) ) {
dialog : : addBoolItem_action ( XLAT ( " edit VR or non-VR settings " ) , set_vr_settings , ' V ' ) ;
if ( set_vr_settings ) dialog : : items . back ( ) . value = " VR " ;
else dialog : : items . back ( ) . value = " non-VR " ;
}
2020-04-17 00:29:03 +00:00
// if(vpmodel == mdBand && sphere)
2020-12-31 01:53:25 +00:00
if ( ! in_perspective_v ( ) & & ! vr_settings ) {
2020-04-16 22:53:58 +00:00
dialog : : addSelItem ( XLAT ( " scale factor " ) , fts ( vpconf . scale ) , ' z ' ) ;
2019-08-14 07:15:57 +00:00
dialog : : add_action ( editScale ) ;
}
2019-08-09 22:58:50 +00:00
2020-11-08 11:09:42 +00:00
if ( abs ( vpconf . alpha - 1 ) > 1e-3 & & vpmodel ! = mdBall & & vpmodel ! = mdHyperboloid & & vpmodel ! = mdHemisphere & & vpmodel ! = mdDisk ) {
2019-08-09 22:58:50 +00:00
dialog : : addBreak ( 50 ) ;
2019-08-10 20:46:45 +00:00
dialog : : addInfo ( " NOTE: this works 'correctly' only if the Poincaré model/stereographic projection is used. " ) ;
2019-08-09 22:58:50 +00:00
dialog : : addBreak ( 50 ) ;
}
2020-10-08 16:22:28 +00:00
if ( among ( vpmodel , mdDisk , mdBall , mdHyperboloid , mdRotatedHyperboles , mdPanini ) ) {
2020-11-01 19:10:08 +00:00
dynamicval < eModel > v ( vpconf . model , vpconf . model ) ;
if ( vpmodel = = mdHyperboloid ) vpconf . model = mdDisk ;
2021-02-01 14:10:03 +00:00
add_edit ( vpconf . alpha ) ;
2019-08-09 22:58:50 +00:00
}
2020-04-17 00:29:03 +00:00
if ( has_orientation ( vpmodel ) ) {
2020-04-16 22:53:58 +00:00
dialog : : addSelItem ( XLAT ( " model orientation " ) , fts ( vpconf . model_orientation ) + " ° " , ' l ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ ] ( ) {
2020-04-16 22:53:58 +00:00
dialog : : editNumber ( vpconf . model_orientation , 0 , 360 , 90 , 0 , XLAT ( " model orientation " ) , " " ) ;
2019-08-09 22:58:50 +00:00
} ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2020-04-16 22:53:58 +00:00
dialog : : addSelItem ( XLAT ( " model orientation (y/z plane) " ) , fts ( vpconf . model_orientation_yz ) + " ° " , ' L ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ ] ( ) {
2020-04-16 22:53:58 +00:00
dialog : : editNumber ( vpconf . model_orientation_yz , 0 , 360 , 90 , 0 , XLAT ( " model orientation (y/z plane) " ) , " " ) ;
2019-08-09 22:58:50 +00:00
} ) ;
}
}
2020-09-11 09:13:18 +00:00
if ( among ( vpmodel , mdPerspective , mdHorocyclic ) & & nil ) {
dialog : : addSelItem ( XLAT ( " model orientation " ) , fts ( vpconf . model_orientation ) + " ° " , ' l ' ) ;
dialog : : add_action ( [ ] ( ) {
dialog : : editNumber ( vpconf . model_orientation , 0 , 360 , 90 , 0 , XLAT ( " model orientation " ) , " " ) ;
} ) ;
dialog : : addSelItem ( XLAT ( " rotational or Heisenberg " ) , fts ( vpconf . rotational_nil ) , ' L ' ) ;
dialog : : add_action ( [ ] ( ) {
dialog : : editNumber ( vpconf . rotational_nil , 0 , 1 , 1 , 1 , XLAT ( " 1 = Heisenberg, 0 = rotational " ) , " " ) ;
} ) ;
}
2020-12-31 01:53:25 +00:00
if ( GDIM = = 3 & & vpmodel ! = mdPerspective & & ! vr_settings ) {
2019-08-09 22:58:50 +00:00
const string cliphelp = XLAT (
" Your view of the 3D model is naturally bounded from four directions by your window. "
" Here, you can also set up similar bounds in the Z direction. Radius of the ball/band "
" models, and the distance from the center to the plane in the halfspace model, are 1. \n \n " ) ;
2020-04-16 22:53:58 +00:00
dialog : : addSelItem ( XLAT ( " near clipping plane " ) , fts ( vpconf . clip_max ) , ' c ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ cliphelp ] ( ) {
2020-04-16 22:53:58 +00:00
dialog : : editNumber ( vpconf . clip_max , - 10 , 10 , 0.2 , 1 , XLAT ( " near clipping plane " ) ,
2019-08-09 22:58:50 +00:00
cliphelp + XLAT ( " Objects with Z coordinate "
" bigger than this parameter are not shown. This is useful with the models which "
" extend infinitely in the Z direction, or if you want things close to your character "
" to be not obscured by things closer to the camera. " ) ) ;
} ) ;
2020-04-16 22:53:58 +00:00
dialog : : addSelItem ( XLAT ( " far clipping plane " ) , fts ( vpconf . clip_min ) , ' C ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ cliphelp ] ( ) {
2020-04-16 22:53:58 +00:00
dialog : : editNumber ( vpconf . clip_min , - 10 , 10 , 0.2 , - 1 , XLAT ( " far clipping plane " ) ,
2019-08-09 22:58:50 +00:00
cliphelp + XLAT ( " Objects with Z coordinate "
" smaller than this parameter are not shown; it also affects the fog effect "
" (near clipping plane = 0% fog, far clipping plane = 100% fog). " ) ) ;
} ) ;
}
2020-04-17 00:29:03 +00:00
if ( vpmodel = = mdPolynomial ) {
2019-08-09 22:58:50 +00:00
dialog : : addSelItem ( XLAT ( " coefficient " ) ,
fts ( polygonal : : coefr [ polygonal : : coefid ] ) , ' x ' ) ;
dialog : : add_action ( [ ] ( ) {
polygonal : : maxcoef = max ( polygonal : : maxcoef , polygonal : : coefid ) ;
int ci = polygonal : : coefid + 1 ;
dialog : : editNumber ( polygonal : : coefr [ polygonal : : coefid ] , - 10 , 10 , .01 / ci / ci , 0 , XLAT ( " coefficient " ) , " " ) ;
} ) ;
dialog : : addSelItem ( XLAT ( " coefficient (imaginary) " ) ,
fts ( polygonal : : coefi [ polygonal : : coefid ] ) , ' y ' ) ;
dialog : : add_action ( [ ] ( ) {
polygonal : : maxcoef = max ( polygonal : : maxcoef , polygonal : : coefid ) ;
int ci = polygonal : : coefid + 1 ;
dialog : : editNumber ( polygonal : : coefi [ polygonal : : coefid ] , - 10 , 10 , .01 / ci / ci , 0 , XLAT ( " coefficient (imaginary) " ) , " " ) ;
} ) ;
dialog : : addSelItem ( XLAT ( " which coefficient " ) , its ( polygonal : : coefid ) , ' n ' ) ;
dialog : : add_action ( [ ] ( ) {
dialog : : editNumber ( polygonal : : coefid , 0 , polygonal : : MSI - 1 , 1 , 0 , XLAT ( " which coefficient " ) , " " ) ;
dialog : : bound_low ( 0 ) ; dialog : : bound_up ( polygonal : : MSI - 1 ) ;
} ) ;
}
2020-04-17 00:29:03 +00:00
if ( vpmodel = = mdHalfplane ) {
2020-04-16 22:53:58 +00:00
dialog : : addSelItem ( XLAT ( " half-plane scale " ) , fts ( vpconf . halfplane_scale ) , ' b ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ ] ( ) {
2020-04-16 22:53:58 +00:00
dialog : : editNumber ( vpconf . halfplane_scale , 0 , 2 , 0.25 , 1 , XLAT ( " half-plane scale " ) , " " ) ;
2019-08-09 22:58:50 +00:00
} ) ;
}
2020-04-17 00:29:03 +00:00
if ( vpmodel = = mdRotatedHyperboles ) {
2020-04-16 22:53:58 +00:00
dialog : : addBoolItem_action ( XLAT ( " use atan to make it finite " ) , vpconf . use_atan , ' x ' ) ;
2019-08-09 22:58:50 +00:00
}
2020-12-31 01:53:25 +00:00
if ( vpmodel = = mdBall & & ! vr_settings ) {
2020-04-16 22:53:58 +00:00
dialog : : addSelItem ( XLAT ( " projection in ball model " ) , fts ( vpconf . ballproj ) , ' x ' ) ;
2019-08-09 22:58:50 +00:00
dialog : : add_action ( [ ] ( ) {
2020-04-16 22:53:58 +00:00
dialog : : editNumber ( vpconf . ballproj , 0 , 100 , .1 , 0 , XLAT ( " projection in ball model " ) ,
2019-08-09 22:58:50 +00:00
" This parameter affects the ball model the same way as the projection parameter affects the disk model. " ) ;
} ) ;
}
2020-04-17 00:29:03 +00:00
if ( vpmodel = = mdPolygonal ) {
2019-08-09 22:58:50 +00:00
dialog : : addSelItem ( XLAT ( " polygon sides " ) , its ( polygonal : : SI ) , ' x ' ) ;
dialog : : add_action ( [ ] ( ) {
dialog : : editNumber ( polygonal : : SI , 3 , 10 , 1 , 4 , XLAT ( " polygon sides " ) , " " ) ;
dialog : : reaction = polygonal : : solve ;
} ) ;
dialog : : addSelItem ( XLAT ( " star factor " ) , fts ( polygonal : : STAR ) , ' y ' ) ;
dialog : : add_action ( [ ] ( ) {
dialog : : editNumber ( polygonal : : STAR , - 1 , 1 , .1 , 0 , XLAT ( " star factor " ) , " " ) ;
dialog : : reaction = polygonal : : solve ;
} ) ;
dialog : : addSelItem ( XLAT ( " degree of the approximation " ) , its ( polygonal : : deg ) , ' n ' ) ;
dialog : : add_action ( [ ] ( ) {
dialog : : editNumber ( polygonal : : deg , 2 , polygonal : : MSI - 1 , 1 , 2 , XLAT ( " degree of the approximation " ) , " " ) ;
dialog : : reaction = polygonal : : solve ;
dialog : : bound_low ( 0 ) ; dialog : : bound_up ( polygonal : : MSI - 1 ) ;
} ) ;
}
2021-02-01 11:50:02 +00:00
if ( is_3d ( vpconf ) & & GDIM = = 2 & & ! vr_settings )
add_edit ( vpconf . ballangle ) ;
2019-08-09 22:58:50 +00:00
2020-12-31 01:53:25 +00:00
if ( vr_settings ) {
dialog : : addSelItem ( XLAT ( " VR: rotate the 3D model " ) , fts ( vpconf . vr_angle ) + " ° " , ' B ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( vpconf . vr_angle , 0 , 90 , 5 , 0 , XLAT ( " VR: rotate the 3D model " ) ,
" How the VR model should be rotated. "
) ;
} ) ;
dialog : : addSelItem ( XLAT ( " VR: shift the 3D model " ) , fts ( vpconf . vr_zshift ) , ' Z ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( vpconf . vr_zshift , 0 , 5 , 0.1 , 1 , XLAT ( " VR: shift the 3D model " ) ,
" How the VR model should be shifted forward, in units. "
" The Poincaré disk has the size of 1 unit. You probably do not want this in perspective projections, but "
" it is useful to see e.g. the Poincaré ball not from the center. "
) ;
} ) ;
dialog : : addSelItem ( XLAT ( " VR: scale the 3D model " ) , fts ( vpconf . vr_scale_factor ) + " m " , ' S ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( vpconf . vr_scale_factor , 0 , 5 , 0.1 , 1 , XLAT ( " VR: scale the 3D model " ) ,
" How the VR model should be scaled. At scale 1, 1 unit = 1 meter. Does not affect perspective projections, "
" where the 'absolute unit' setting is used instead. "
) ;
} ) ;
}
2021-02-01 11:50:02 +00:00
if ( vpmodel = = mdHyperboloid )
add_edit ( vpconf . top_z ) ;
2019-08-09 22:58:50 +00:00
2021-02-01 11:50:02 +00:00
if ( has_transition ( vpmodel ) )
add_edit ( vpconf . model_transition ) ;
2019-08-09 22:58:50 +00:00
2021-02-01 11:50:02 +00:00
if ( among ( vpmodel , mdJoukowsky , mdJoukowskyInverted , mdSpiral ) & & GDIM = = 2 )
add_edit ( vpconf . skiprope ) ;
2019-08-09 22:58:50 +00:00
2021-02-01 11:50:02 +00:00
if ( vpmodel = = mdHemisphere & & euclid )
add_edit ( vpconf . euclid_to_sphere ) ;
2019-08-09 22:58:50 +00:00
2021-02-01 11:50:02 +00:00
if ( among ( vpmodel , mdTwoPoint , mdSimulatedPerspective , mdTwoHybrid ) )
add_edit ( vpconf . twopoint_param ) ;
2019-08-09 22:58:50 +00:00
2021-02-01 11:50:02 +00:00
if ( vpmodel = = mdFisheye )
add_edit ( vpconf . fisheye_param ) ;
2020-11-01 19:10:08 +00:00
2021-02-01 11:50:02 +00:00
if ( vpmodel = = mdHyperboloid )
add_edit ( pconf . show_hyperboloid_flat ) ;
2019-10-05 10:32:16 +00:00
2021-02-01 11:50:02 +00:00
if ( vpmodel = = mdCollignon )
add_edit ( vpconf . collignon_parameter ) ;
2019-08-14 16:59:21 +00:00
2020-09-16 15:33:48 +00:00
if ( vpmodel = = mdMiller ) {
dialog : : addSelItem ( XLAT ( " parameter " ) , fts ( vpconf . miller_parameter ) , ' b ' ) ;
dialog : : add_action ( [ ] ( ) {
dialog : : editNumber ( vpconf . miller_parameter , - 1 , 1 , .1 , 4 / 5. , XLAT ( " parameter " ) ,
" The Miller projection is obtained by multiplying the latitude by 4/5, using Mercator projection, and then multiplying Y by 5/4. "
" Here you can change this parameter. "
) ;
} ) ;
}
2021-02-01 11:50:02 +00:00
if ( among ( vpmodel , mdLoximuthal , mdRetroHammer , mdRetroCraig ) )
add_edit ( vpconf . loximuthal_parameter ) ;
2020-09-16 15:33:48 +00:00
2021-02-01 11:50:02 +00:00
if ( among ( vpmodel , mdAitoff , mdHammer , mdWinkelTripel ) )
add_edit ( vpconf . aitoff_parameter ) ;
2020-09-16 15:33:48 +00:00
2021-02-01 11:50:02 +00:00
if ( vpmodel = = mdWinkelTripel )
add_edit ( vpconf . winkel_parameter ) ;
2020-09-16 15:33:48 +00:00
2020-04-17 00:29:03 +00:00
if ( vpmodel = = mdSpiral & & ! euclid ) {
2021-02-01 11:50:02 +00:00
add_edit ( vpconf . spiral_angle ) ;
2019-08-09 22:58:50 +00:00
2021-02-01 11:50:02 +00:00
add_edit (
2020-04-16 22:53:58 +00:00
sphere ? vpconf . sphere_spiral_multiplier :
ring_not_spiral ? vpconf . right_spiral_multiplier :
2021-02-01 11:50:02 +00:00
vpconf . any_spiral_multiplier
) ;
2019-08-09 22:58:50 +00:00
2021-02-01 11:50:02 +00:00
add_edit ( vpconf . spiral_cone ) ;
2019-08-09 22:58:50 +00:00
}
2020-04-17 00:29:03 +00:00
if ( vpmodel = = mdSpiral & & euclid ) {
2021-02-01 11:50:02 +00:00
add_edit ( vpconf . spiral_x ) ;
add_edit ( vpconf . spiral_y ) ;
2019-11-27 00:01:20 +00:00
if ( euclid & & quotient ) {
2019-08-09 22:58:50 +00:00
dialog : : addSelItem ( XLAT ( " match the period " ) , its ( spiral_id ) , ' n ' ) ;
dialog : : add_action ( match_torus_period ) ;
}
}
2021-02-01 11:50:02 +00:00
add_edit ( vpconf . stretch ) ;
2019-11-09 12:14:42 +00:00
2021-02-01 11:50:02 +00:00
if ( product_model ( vpmodel ) )
add_edit ( vpconf . product_z_scale ) ;
2019-08-09 22:58:50 +00:00
2020-07-03 12:42:33 +00:00
# if CAP_GL
2019-08-09 22:58:50 +00:00
dialog : : addBoolItem ( XLAT ( " use GPU to compute projections " ) , vid . consider_shader_projection , ' G ' ) ;
2019-10-21 20:34:20 +00:00
bool shaderside_projection = get_shader_flags ( ) & SF_DIRECT ;
2019-08-09 22:58:50 +00:00
if ( vid . consider_shader_projection & & ! shaderside_projection )
dialog : : lastItem ( ) . value = XLAT ( " N/A " ) ;
2020-04-17 00:29:03 +00:00
if ( vid . consider_shader_projection & & shaderside_projection & & vpmodel )
2019-08-09 22:58:50 +00:00
dialog : : lastItem ( ) . value + = XLAT ( " (2D only) " ) ;
dialog : : add_action ( [ ] { vid . consider_shader_projection = ! vid . consider_shader_projection ; } ) ;
2020-07-03 12:42:33 +00:00
# endif
2019-08-09 22:58:50 +00:00
menuitem_sightrange ( ' R ' ) ;
dialog : : addBreak ( 100 ) ;
dialog : : addItem ( XLAT ( " history mode " ) , ' a ' ) ;
2019-08-14 07:15:57 +00:00
dialog : : add_action_push ( history : : history_menu ) ;
2019-08-09 22:58:50 +00:00
# if CAP_RUG
2020-04-17 00:21:44 +00:00
if ( GDIM = = 2 | | rug : : rugged ) {
2019-08-14 07:15:57 +00:00
dialog : : addItem ( XLAT ( " hypersian rug mode " ) , ' u ' ) ;
dialog : : add_action_push ( rug : : show ) ;
}
2019-08-09 22:58:50 +00:00
# endif
dialog : : addBack ( ) ;
dialog : : display ( ) ;
mouseovers = XLAT ( " see http://www.roguetemple.com/z/hyper/models.php " ) ;
}
2021-02-01 10:20:22 +00:00
EX void quick_model ( ) {
cmode = sm : : CENTER ;
gamescreen ( 1 ) ;
dialog : : init ( " models & projections " ) ;
if ( WDIM = = 2 & & ! euclid ) {
dialog : : addItem ( XLAT ( hyperbolic ? " Gans model " : " orthographic projection " ) , ' 1 ' ) ;
dialog : : add_action ( [ ] { if ( rug : : rugged ) rug : : close ( ) ; pconf . alpha = 999 ; pconf . scale = 998 ; pconf . xposition = pconf . yposition = 0 ; popScreen ( ) ; } ) ;
dialog : : addItem ( XLAT ( hyperbolic ? " Poincaré model " : " stereographic projection " ) , ' 2 ' ) ;
dialog : : add_action ( [ ] { if ( rug : : rugged ) rug : : close ( ) ; pconf . alpha = 1 ; pconf . scale = 1 ; pconf . xposition = pconf . yposition = 0 ; popScreen ( ) ; } ) ;
dialog : : addItem ( XLAT ( hyperbolic ? " Beltrami-Klein model " : " gnomonic projection " ) , ' 3 ' ) ;
dialog : : add_action ( [ ] { if ( rug : : rugged ) rug : : close ( ) ; pconf . alpha = 0 ; pconf . scale = 1 ; pconf . xposition = pconf . yposition = 0 ; popScreen ( ) ; } ) ;
if ( sphere ) {
dialog : : addItem ( XLAT ( " stereographic projection " ) + " " + XLAT ( " (zoomed out) " ) , ' 4 ' ) ;
dialog : : add_action ( [ ] { if ( rug : : rugged ) rug : : close ( ) ; pconf . alpha = 1 ; pconf . scale = 0.4 ; pconf . xposition = pconf . yposition = 0 ; popScreen ( ) ; } ) ;
}
if ( hyperbolic ) {
dialog : : addItem ( XLAT ( " Gans model " ) + " " + XLAT ( " (zoomed out) " ) , ' 4 ' ) ;
dialog : : add_action ( [ ] { if ( rug : : rugged ) rug : : close ( ) ; pconf . alpha = 999 ; pconf . scale = 499 ; pconf . xposition = pconf . yposition = 0 ; popScreen ( ) ; } ) ;
dialog : : addItem ( XLAT ( " Hypersian rug " ) , ' u ' ) ;
dialog : : add_action ( [ ] {
if ( rug : : rugged ) pushScreen ( rug : : show ) ;
else {
pconf . alpha = 1 , pconf . scale = 1 ; if ( ! rug : : rugged ) rug : : init ( ) ; popScreen ( ) ;
}
} ) ;
}
}
else if ( WDIM = = 2 & & euclid ) {
auto zoom_to = [ ] ( ld s ) {
pconf . xposition = pconf . yposition = 0 ;
ld maxs = 0 ;
auto & cd = current_display ;
for ( auto & p : gmatrix ) for ( int i = 0 ; i < p . first - > type ; i + + ) {
shiftpoint h = tC0 ( p . second * currentmap - > adj ( p . first , i ) ) ;
hyperpoint onscreen ;
applymodel ( h , onscreen ) ;
maxs = max ( maxs , onscreen [ 0 ] / cd - > xsize ) ;
maxs = max ( maxs , onscreen [ 1 ] / cd - > ysize ) ;
}
pconf . alpha = 1 ;
pconf . scale = s * pconf . scale / 2 / maxs / cd - > radius ;
popScreen ( ) ;
} ;
dialog : : addItem ( XLAT ( " zoom 2x " ) , ' 1 ' ) ;
dialog : : add_action ( [ zoom_to ] { zoom_to ( 2 ) ; } ) ;
dialog : : addItem ( XLAT ( " zoom 1x " ) , ' 2 ' ) ;
dialog : : add_action ( [ zoom_to ] { zoom_to ( 1 ) ; } ) ;
dialog : : addItem ( XLAT ( " zoom 0.5x " ) , ' 3 ' ) ;
dialog : : add_action ( [ zoom_to ] { zoom_to ( .5 ) ; } ) ;
}
else if ( WDIM = = 3 ) {
auto & ysh = ( WDIM = = 2 ? vid . camera : vid . yshift ) ;
dialog : : addItem ( XLAT ( " first-person perspective " ) , ' 1 ' ) ;
dialog : : add_action ( [ & ysh ] { ysh = 0 ; vid . sspeed = 0 ; popScreen ( ) ; } ) ;
dialog : : addItem ( XLAT ( " fixed point of view " ) , ' 2 ' ) ;
dialog : : add_action ( [ & ysh ] { ysh = 0 ; vid . sspeed = - 10 ; popScreen ( ) ; } ) ;
dialog : : addItem ( XLAT ( " third-person perspective " ) , ' 3 ' ) ;
dialog : : add_action ( [ & ysh ] { ysh = 1 ; vid . sspeed = 0 ; popScreen ( ) ; } ) ;
}
dialog : : addItem ( XLAT ( " advanced projections " ) , ' a ' ) ;
dialog : : add_action_push ( model_menu ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
2019-08-09 22:58:50 +00:00
# if CAP_COMMANDLINE
2019-10-05 11:04:18 +00:00
eModel read_model ( const string & ss ) {
2019-10-06 10:11:01 +00:00
for ( int i = 0 ; i < isize ( mdinf ) ; i + + ) {
2019-10-05 11:04:18 +00:00
if ( appears ( mdinf [ i ] . name_hyperbolic , ss ) ) return eModel ( i ) ;
if ( appears ( mdinf [ i ] . name_euclidean , ss ) ) return eModel ( i ) ;
if ( appears ( mdinf [ i ] . name_spherical , ss ) ) return eModel ( i ) ;
}
return eModel ( atoi ( ss . c_str ( ) ) ) ;
}
2019-08-09 22:58:50 +00:00
int readArgs ( ) {
using namespace arg ;
if ( 0 ) ;
else if ( argis ( " -els " ) ) {
shift_arg_formula ( history : : extra_line_steps ) ;
}
else if ( argis ( " -stretch " ) ) {
2020-04-16 22:53:58 +00:00
PHASEFROM ( 2 ) ; shift_arg_formula ( vpconf . stretch ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -PM " ) ) {
2020-04-16 22:53:58 +00:00
PHASEFROM ( 2 ) ; shift ( ) ; vpconf . model = read_model ( args ( ) ) ;
2020-04-17 00:29:03 +00:00
if ( vpconf . model = = mdFormula ) {
2020-04-16 22:53:58 +00:00
shift ( ) ; vpconf . basic_model = eModel ( argi ( ) ) ;
shift ( ) ; vpconf . formula = args ( ) ;
2019-08-09 22:58:50 +00:00
}
}
else if ( argis ( " -ballangle " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . ballangle ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -topz " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . top_z ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -twopoint " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . twopoint_param ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -hp " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . halfplane_scale ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -mori " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . model_orientation ) ;
2019-08-09 22:58:50 +00:00
}
2020-12-28 21:01:29 +00:00
else if ( argis ( " -mets " ) ) {
PHASEFROM ( 2 ) ;
shift_arg_formula ( vpconf . euclid_to_sphere ) ;
}
else if ( argis ( " -mhyp " ) ) {
PHASEFROM ( 2 ) ;
shift_arg_formula ( vpconf . hyperboloid_scaling ) ;
}
else if ( argis ( " -mdepth " ) ) {
PHASEFROM ( 2 ) ;
shift_arg_formula ( vpconf . depth_scaling ) ;
}
2020-09-11 09:13:18 +00:00
else if ( argis ( " -mnil " ) ) {
PHASEFROM ( 2 ) ;
shift_arg_formula ( vpconf . rotational_nil ) ;
}
2019-08-09 22:58:50 +00:00
else if ( argis ( " -mori2 " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . model_orientation ) ;
shift_arg_formula ( vpconf . model_orientation_yz ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -crot " ) ) {
PHASEFROM ( 2 ) ;
shift_arg_formula ( models : : rotation ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) shift_arg_formula ( models : : rotation_xz ) ;
if ( GDIM = = 3 ) shift_arg_formula ( models : : rotation_xy2 ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -clip " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . clip_min ) ;
shift_arg_formula ( vpconf . clip_max ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -mtrans " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . model_transition ) ;
2019-08-09 22:58:50 +00:00
}
2020-09-16 15:34:08 +00:00
else if ( argis ( " -mparam " ) ) {
PHASEFROM ( 2 ) ;
if ( pmodel = = mdCollignon ) shift_arg_formula ( vpconf . collignon_parameter ) ;
else if ( pmodel = = mdMiller ) shift_arg_formula ( vpconf . miller_parameter ) ;
2020-10-08 16:22:28 +00:00
else if ( among ( pmodel , mdLoximuthal , mdRetroCraig , mdRetroHammer ) ) shift_arg_formula ( vpconf . loximuthal_parameter ) ;
2020-09-16 15:34:08 +00:00
else if ( among ( pmodel , mdAitoff , mdHammer , mdWinkelTripel ) ) shift_arg_formula ( vpconf . aitoff_parameter ) ;
if ( pmodel = = mdWinkelTripel ) shift_arg_formula ( vpconf . winkel_parameter ) ;
}
2019-08-09 22:58:50 +00:00
else if ( argis ( " -sang " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . spiral_angle ) ;
2019-08-09 22:58:50 +00:00
if ( sphere )
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . sphere_spiral_multiplier ) ;
else if ( vpconf . spiral_angle = = 90 )
shift_arg_formula ( vpconf . right_spiral_multiplier ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -ssm " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . any_spiral_multiplier ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -scone " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . spiral_cone ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -sxy " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . spiral_x ) ;
shift_arg_formula ( vpconf . spiral_y ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -mob " ) ) {
PHASEFROM ( 2 ) ;
2020-04-16 22:53:58 +00:00
shift_arg_formula ( vpconf . skiprope ) ;
2019-08-09 22:58:50 +00:00
}
2020-11-01 20:20:54 +00:00
else if ( argis ( " -palpha " ) ) {
PHASEFROM ( 2 ) ;
shift_arg_formula ( panini_alpha , reset_all_shaders ) ;
}
2019-08-09 22:58:50 +00:00
else if ( argis ( " -zoom " ) ) {
2020-04-16 22:53:58 +00:00
PHASEFROM ( 2 ) ; shift_arg_formula ( vpconf . scale ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -alpha " ) ) {
2020-04-16 22:53:58 +00:00
PHASEFROM ( 2 ) ; shift_arg_formula ( vpconf . alpha ) ;
2019-08-09 22:58:50 +00:00
}
else if ( argis ( " -d:model " ) )
launch_dialog ( model_menu ) ;
else if ( argis ( " -d:formula " ) ) {
launch_dialog ( ) ;
edit_formula ( ) ;
}
else if ( argis ( " -d:match " ) ) {
launch_dialog ( match_torus_period ) ;
edit_formula ( ) ;
}
else return 1 ;
return 0 ;
}
2021-01-31 17:45:17 +00:00
void add_model_config ( ) {
addsaver ( polygonal : : SI , " polygon sides " ) ;
2021-02-01 00:45:10 +00:00
param_f ( polygonal : : STAR , " star " , " polygon star factor " ) ;
2021-01-31 17:45:17 +00:00
addsaver ( polygonal : : deg , " polygonal degree " ) ;
addsaver ( polygonal : : maxcoef , " polynomial degree " ) ;
for ( int i = 0 ; i < polygonal : : MSI ; i + + ) {
addsaver ( polygonal : : coefr [ i ] , " polynomial " + its ( i ) + " .real " ) ;
addsaver ( polygonal : : coefi [ i ] , " polynomial " + its ( i ) + " .imag " ) ;
}
2021-02-01 00:45:10 +00:00
param_f ( models : : rotation , " rotation " , " conformal rotation " ) ;
2021-01-31 17:45:17 +00:00
addsaver ( models : : rotation_xz , " conformal rotation_xz " ) ;
addsaver ( models : : rotation_xy2 , " conformal rotation_2 " ) ;
addsaver ( models : : do_rotate , " conformal rotation mode " , 1 ) ;
2021-02-01 11:50:02 +00:00
param_f ( pconf . halfplane_scale , " hp " , " halfplane scale " , 1 ) ;
2021-02-01 11:03:55 +00:00
auto add_all = [ & ] ( projection_configuration & p , string pp , string sp ) {
2021-02-01 14:10:03 +00:00
2021-02-01 11:03:55 +00:00
bool rug = pp ! = " " ;
2021-02-01 14:22:47 +00:00
dynamicval < function < bool ( ) > > ds ( auto_restrict ) ;
auto_restrict = [ & p ] { return & vpconf = = & p ; } ;
2021-02-01 14:10:03 +00:00
addsaverenum ( p . model , pp + " used model " , mdDisk ) ;
param_custom ( pmodel , " projection|Poincare|Klein|half-plane|perspective " , menuitem_projection , ' 1 ' ) ;
2021-02-01 11:03:55 +00:00
param_f ( p . model_orientation , pp + " mori " , sp + " model orientation " , 0 ) ;
param_f ( p . model_orientation_yz , pp + " mori_yz " , sp + " model orientation-yz " , 0 ) ;
2021-02-01 11:50:02 +00:00
param_f ( p . top_z , sp + " topz " , 5 )
- > editable ( 1 , 20 , .25 , " maximum z coordinate to show " , " maximum z coordinate to show " , ' l ' ) ;
param_f ( p . model_transition , pp + " mtrans " , sp + " model transition " , 1 )
- > editable ( 0 , 1 , .1 , " model transition " ,
" You can change this parameter for a transition from another model to this one. " , ' t ' ) ;
2021-02-01 11:03:55 +00:00
param_f ( p . rotational_nil , sp + " rotnil " , 1 ) ;
param_f ( p . clip_min , pp + " clipmin " , sp + " clip-min " , rug ? - 100 : - 1 ) ;
param_f ( p . clip_max , pp + " clipmax " , sp + " clip-max " , rug ? + 10 : + 1 ) ;
2021-02-01 11:50:02 +00:00
param_f ( p . euclid_to_sphere , pp + " ets " , sp + " euclid to sphere projection " , 1.5 )
- > editable ( 1e-1 , 10 , .1 , " ETS parameter " , " Stereographic projection to a sphere. Choose the radius of the sphere. " , ' l ' )
- > set_sets ( dialog : : scaleLog ) ;
param_f ( p . twopoint_param , pp + " twopoint " , sp + " twopoint parameter " , 1 )
- > editable ( 1e-3 , 10 , .1 , " two-point parameter " , " In two-point-based models, this parameter gives the distance from each of the two points to the center. " , ' b ' )
- > set_sets ( dialog : : scaleLog )
;
param_f ( p . fisheye_param , pp + " fisheye " , sp + " fisheye parameter " , 1 )
- > editable ( 1e-3 , 10 , .1 , " fisheye parameter " , " Size of the fish eye. " , ' b ' )
- > set_sets ( dialog : : scaleLog ) ;
param_f ( p . stretch , pp + " stretch " , 1 )
- > editable ( 0 , 10 , .1 , " vertical stretch " , " Vertical stretch factor. " , ' s ' )
- > set_extra ( stretch_extra ) ;
param_f ( p . product_z_scale , pp + " zstretch " )
- > editable ( 0.1 , 10 , 0.1 , " product Z stretch " , " " , ' Z ' ) ;
2021-02-01 11:03:55 +00:00
2021-02-01 11:50:02 +00:00
param_f ( p . collignon_parameter , pp + " collignon " , sp + " collignon-parameter " , 1 )
- > editable ( - 1 , 1 , .1 , " Collignon parameter " , " " , ' b ' )
- > modif ( [ ] ( float_setting * f ) {
f - > unit = vpconf . collignon_reflected ? " (r) " : " " ;
} )
- > set_extra ( [ & p ] {
add_edit ( p . collignon_reflected ) ;
} ) ;
param_b ( p . collignon_reflected , sp + " collignon-reflect " , false )
- > editable ( " Collignon reflect " , ' R ' ) ;
2021-02-01 11:03:55 +00:00
2021-02-01 11:50:02 +00:00
param_f ( p . aitoff_parameter , sp + " aitoff " )
- > editable ( - 1 , 1 , .1 , " Aitoff parameter " ,
" The Aitoff projection is obtained by multiplying the longitude by 1/2, using azimuthal equidistant projection, and then multiplying X by 1/2. "
" Hammer projection is similar but equi-area projection is used instead. "
" Here you can change this parameter. " , ' b ' ) ;
2021-02-01 11:03:55 +00:00
param_f ( p . miller_parameter , sp + " miller " ) ;
2021-02-01 11:50:02 +00:00
param_f ( p . loximuthal_parameter , sp + " loximuthal " )
- > editable ( - M_PI / 2 , M_PI / 2 , .1 , " loximuthal parameter " ,
" Loximuthal is similar to azimuthal equidistant, but based on loxodromes (lines of constant geographic direction) rather than geodesics. "
" The loximuthal projection maps (the shortest) loxodromes to straight lines of the same length, going through the starting point. "
" This setting changes the latitude of the starting point. \n \n "
" In retroazimuthal projections, a point is drawn at such a point that the azimuth *from* that point to the chosen central point is correct. "
" For example, if you should move east, the point is drawn to the right. This parameter is the latitude of the central point. "
" \n \n (In hyperbolic geometry directions are assigned according to the Lobachevsky coordinates.) " , ' b '
) ;
param_f ( p . winkel_parameter , sp + " winkel " )
- > editable ( - 1 , 1 , .1 , " Winkel Tripel mixing " ,
" The Winkel Tripel projection is the average of Aitoff projection and equirectangular projection. Here you can change the proportion. " , ' B ' ) ;
2021-02-01 11:03:55 +00:00
2021-02-01 11:50:02 +00:00
param_b ( p . show_hyperboloid_flat , sp + " hyperboloid-flat " , true )
- > editable ( " show flat " , ' b ' ) ;
2021-02-01 11:03:55 +00:00
2021-02-01 11:50:02 +00:00
param_f ( p . skiprope , sp + " mobius " , 0 )
- > editable ( 0 , 360 , 15 , " Möbius transformations " , " " , ' S ' ) - > unit = " ° " ;
2021-02-01 11:03:55 +00:00
addsaver ( p . formula , sp + " formula " ) ;
addsaverenum ( p . basic_model , sp + " basic model " ) ;
addsaver ( p . use_atan , sp + " use_atan " ) ;
2021-02-01 11:50:02 +00:00
param_f ( p . spiral_angle , sp + " sang " )
- > editable ( 0 , 360 , 15 , " spiral angle " , " set to 90° for the ring projection " , ' x ' )
- > unit = " ° " ;
param_f ( p . spiral_x , sp + " spiralx " )
- > editable ( - 20 , 20 , 1 , " spiral period: x " , " " , ' x ' ) ;
param_f ( p . spiral_y , sp + " spiraly " )
- > editable ( - 20 , 20 , 1 , " spiral period: y " , " " , ' y ' ) ;
2021-02-01 11:03:55 +00:00
param_f ( p . scale , sp + " scale " , 1 ) ;
param_f ( p . xposition , sp + " xposition " , 0 ) ;
param_f ( p . yposition , sp + " yposition " , 0 ) ;
2021-02-01 14:10:03 +00:00
addsaver ( p . alpha , sp + " projection " , 1 ) ;
2021-02-01 14:22:47 +00:00
param_custom ( p . alpha , sp + " projection " , menuitem_projection_distance , ' p ' )
- > help_text = " projection distance|Gans Klein Poincare orthographic stereographic " ;
2021-02-01 14:10:03 +00:00
2021-02-01 11:03:55 +00:00
param_f ( p . camera_angle , pp + " cameraangle " , sp + " camera angle " , 0 ) ;
addsaver ( p . ballproj , sp + " ballproj " , 1 ) ;
2021-02-01 11:50:02 +00:00
param_f ( p . ballangle , pp + " ballangle " , sp + " ball angle " , 20 )
- > editable ( 0 , 90 , 5 , " camera rotation in 3D models " ,
" Rotate the camera in 3D models (ball model, hyperboloid, and hemisphere). "
" Note that hyperboloid and hemisphere models are also available in the "
" Hypersian Rug surfaces menu, but they are rendered differently there -- "
" by making a flat picture first, then mapping it to a surface. "
" This makes the output better in some ways, but 3D effects are lost. "
" Hypersian Rug model also allows more camera freedom. " ,
' b ' )
- > unit = " ° " ;
string help =
" This parameter has a bit different scale depending on the settings: \n "
" (1) in spherical geometry (with spiral angle=90°, 1 produces a stereographic projection) \n "
" (2) in hyperbolic geometry, with spiral angle being +90° or -90° \n "
" (3) in hyperbolic geometry, with other spiral angles (1 makes the bands fit exactly) " ;
param_f ( p . sphere_spiral_multiplier , " sphere_spiral_multiplier " )
- > editable ( 0 , 10 , .1 , " sphere spiral multiplier " , help , ' M ' ) - > unit = " ° " ;
param_f ( p . right_spiral_multiplier , " right_spiral_multiplier " )
- > editable ( 0 , 10 , .1 , " right spiral multiplier " , help , ' M ' ) - > unit = " ° " ;
param_f ( p . any_spiral_multiplier , " any_spiral_multiplier " )
- > editable ( 0 , 10 , .1 , " any spiral multiplier " , help , ' M ' ) - > unit = " ° " ;
param_f ( p . spiral_cone , " spiral_cone " )
- > editable ( 0 , 360 , - 45 , " spiral cone " , " " , ' C ' ) - > unit = " ° " ;
2021-02-01 11:03:55 +00:00
} ;
add_all ( pconf , " " , " " ) ;
add_all ( vid . rug_config , " rug_ " , " rug- " ) ;
2021-01-31 17:45:17 +00:00
}
2019-08-09 22:58:50 +00:00
2021-01-31 17:45:17 +00:00
auto hookArg = addHook ( hooks_args , 100 , readArgs ) + addHook ( hooks_config , 100 , add_model_config ) ;
2019-08-09 22:58:50 +00:00
# endif
}
}