2016-08-26 09:58:03 +00:00
// Hyperbolic Rogue
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// implementation of the Hypersian Rug mode
2017-11-06 18:24:02 +00:00
2017-07-22 23:33:27 +00:00
# if CAP_RUG
2016-08-26 09:58:03 +00:00
# define TEXTURESIZE (texturesize)
# define HTEXTURESIZE (texturesize / 2)
namespace rug {
2018-02-03 13:35:06 +00:00
int when_enabled ;
2017-12-29 11:54:50 +00:00
struct rug_exception { } ;
2017-12-27 05:31:47 +00:00
bool fast_euclidean = true ;
2017-12-27 09:52:54 +00:00
bool good_shape ;
2017-12-25 22:47:57 +00:00
2017-12-27 09:52:54 +00:00
ld modelscale = 1 ;
2017-12-27 17:53:00 +00:00
ld model_distance = 2 ;
2017-11-06 18:24:02 +00:00
2017-12-25 22:47:57 +00:00
eGeometry gwhere = gEuclid ;
2017-12-27 18:55:00 +00:00
# define USING_NATIVE_GEOMETRY dynamicval<eGeometry> gw(geometry, gwhere == gElliptic ? gSphere : gwhere)
2016-08-26 09:58:03 +00:00
// hypersian rug datatypes and globals
//-------------------------------------
bool rugged = false ;
bool genrug = false ;
2017-12-27 09:52:54 +00:00
int vertex_limit = 20000 ;
2016-08-26 09:58:03 +00:00
bool renderonce = false ;
bool rendernogl = false ;
int texturesize = 1024 ;
2017-07-16 21:00:55 +00:00
ld scale = 1 ;
2016-08-26 09:58:03 +00:00
2017-12-27 09:52:54 +00:00
ld err_zero = 1e-3 , err_zero_current , current_total_error ;
2017-12-25 22:47:57 +00:00
2016-08-26 09:58:03 +00:00
int queueiter , qvalid , dt ;
struct edge {
struct rugpoint * target ;
double len ;
} ;
struct rugpoint {
double x1 , y1 ;
bool valid ;
bool inqueue ;
2017-03-23 10:53:57 +00:00
double dist ;
2018-01-29 15:30:55 +00:00
hyperpoint h ; // point in the represented space
hyperpoint flat ; // point in the native space, in azeq
2018-02-03 12:41:49 +00:00
hyperpoint precompute ;
2016-08-26 09:58:03 +00:00
vector < edge > edges ;
2017-12-27 05:31:47 +00:00
// Find-Union algorithm
2018-01-05 16:18:37 +00:00
rugpoint * glue ;
2017-12-27 05:31:47 +00:00
rugpoint * getglue ( ) {
return glue ? ( glue = glue - > getglue ( ) ) : this ;
}
hyperpoint & glueflat ( ) {
return glue - > flat ;
}
2018-01-05 16:18:37 +00:00
rugpoint ( ) { glue = NULL ; }
2017-12-27 05:31:47 +00:00
void glueto ( rugpoint * x ) {
x = x - > getglue ( ) ;
auto y = getglue ( ) ;
if ( x ! = y ) y - > glue = x ;
}
2016-08-26 09:58:03 +00:00
} ;
2018-01-29 15:30:21 +00:00
rugpoint * finger_center ;
ld finger_range = .1 ;
ld finger_force = 1 ;
2018-01-29 15:28:06 +00:00
2016-08-26 09:58:03 +00:00
struct triangle {
rugpoint * m [ 3 ] ;
triangle ( rugpoint * m1 , rugpoint * m2 , rugpoint * m3 ) {
m [ 0 ] = m1 ; m [ 1 ] = m2 ; m [ 2 ] = m3 ;
}
} ;
vector < rugpoint * > points ;
vector < triangle > triangles ;
2018-02-04 00:04:29 +00:00
bool rug_perspective = ISANDROID ;
2017-12-25 22:47:57 +00:00
// extra geometry functions
//--------------------------
// returns a matrix M
// such that inverse(M) * h1 = ( |h1|, 0, 0) and inverse(M) * h2 = ( .., .., 0)
transmatrix orthonormalize ( hyperpoint h1 , hyperpoint h2 ) {
using namespace hyperpoint_vec ;
2017-12-27 09:52:54 +00:00
hyperpoint vec [ 3 ] = { h1 , h2 , h1 ^ h2 } ;
2017-12-25 22:47:57 +00:00
2017-12-27 09:52:54 +00:00
for ( int i = 0 ; i < 3 ; i + + ) {
for ( int j = 0 ; j < i ; j + + ) vec [ i ] - = vec [ j ] * ( vec [ i ] | vec [ j ] ) ;
if ( zero3 ( vec [ i ] ) ) {
// 'random' direction
vec [ i ] = hpxyz ( 1.12 , 1.512 + i , 1.12904 + i ) ;
for ( int j = 0 ; j < i ; j + + ) vec [ i ] - = vec [ j ] * ( vec [ i ] | vec [ j ] ) ;
}
vec [ i ] / = hypot3 ( vec [ i ] ) ;
}
2017-12-25 22:47:57 +00:00
transmatrix M ;
for ( int i = 0 ; i < 3 ; i + + ) for ( int j = 0 ; j < 3 ; j + + )
M [ i ] [ j ] = vec [ j ] [ i ] ;
return M ;
}
hyperpoint azeq_to_hyperboloid ( hyperpoint h ) {
if ( abs ( h [ 2 ] ) > 1e-4 ) printf ( " Error: h[2] = %lf \n " , h [ 2 ] ) ;
if ( euclid ) {
h [ 2 ] = 1 ;
return h ;
}
ld d = hypot ( h [ 0 ] , h [ 1 ] ) ;
if ( d = = 0 ) {
h [ 2 ] = 1 ;
return h ;
}
if ( sphere ) {
2017-12-27 09:52:54 +00:00
ld d0 = d ? d : 1 ;
h [ 0 ] = sin ( d ) * h [ 0 ] / d0 ;
h [ 1 ] = sin ( d ) * h [ 1 ] / d0 ;
2017-12-25 22:47:57 +00:00
h [ 2 ] = cos ( d ) ;
}
else {
2017-12-27 09:52:54 +00:00
ld d0 = d ? d : 1 ;
h [ 0 ] = sinh ( d ) * h [ 0 ] / d0 ;
h [ 1 ] = sinh ( d ) * h [ 1 ] / d0 ;
2017-12-25 22:47:57 +00:00
h [ 2 ] = cosh ( d ) ;
}
return h ;
}
hyperpoint hyperboloid_to_azeq ( hyperpoint h ) {
if ( euclid ) {
h [ 2 ] = 0 ;
return h ;
}
else {
ld d = hdist0 ( h ) ;
if ( d = = 0 ) { h [ 2 ] = 0 ; return h ; }
2017-12-27 09:52:54 +00:00
ld d2 = hypot2 ( h ) ;
2017-12-25 22:47:57 +00:00
if ( d2 = = 0 ) { h [ 2 ] = 0 ; return h ; }
h [ 0 ] = d * h [ 0 ] / d2 ;
h [ 1 ] = d * h [ 1 ] / d2 ;
h [ 2 ] = 0 ;
return h ;
}
}
2017-12-27 20:08:31 +00:00
struct normalizer {
transmatrix M , Mi ;
dynamicval < eGeometry > gw ;
normalizer ( hyperpoint h1 , hyperpoint h2 ) : gw ( geometry , gwhere = = gElliptic ? gSphere : gwhere ) {
M = orthonormalize ( h1 , h2 ) ;
Mi = inverse ( M ) ;
}
hyperpoint operator ( ) ( hyperpoint h ) { return azeq_to_hyperboloid ( Mi * h ) ; }
hyperpoint operator [ ] ( hyperpoint h ) { return M * hyperboloid_to_azeq ( h ) ; }
} ;
2017-12-27 09:52:54 +00:00
void push_point ( hyperpoint & h , int coord , ld val ) {
if ( fast_euclidean & & gwhere = = gEuclid )
h [ coord ] + = val ;
else if ( ! val ) return ;
else {
// if(zero3(h)) { h[0] = 1e-9; h[1] = 1e-10; h[2] = 1e-11; }
2017-12-27 20:08:31 +00:00
normalizer n ( hpxyz ( coord = = 0 , coord = = 1 , coord = = 2 ) , h ) ;
hyperpoint f = n ( h ) ;
h = n [ xpush ( val ) * f ] ;
2017-12-25 22:47:57 +00:00
}
}
2017-12-27 09:52:54 +00:00
void push_all_points ( int coord , ld val ) {
if ( ! val ) return ;
else for ( int i = 0 ; i < size ( points ) ; i + + )
push_point ( points [ i ] - > flat , coord , val ) ;
}
2017-12-25 22:47:57 +00:00
2016-08-26 09:58:03 +00:00
// construct the graph
//---------------------
int hyprand ;
2017-03-23 10:53:57 +00:00
rugpoint * addRugpoint ( hyperpoint h , double dist ) {
2016-08-26 09:58:03 +00:00
rugpoint * m = new rugpoint ;
m - > h = h ;
2017-12-25 22:47:57 +00:00
/*
2018-02-03 12:41:49 +00:00
ld tz = vid . alpha + h [ 2 ] ;
2016-08-26 09:58:03 +00:00
m - > x1 = ( 1 + h [ 0 ] / tz ) / 2 ;
m - > y1 = ( 1 + h [ 1 ] / tz ) / 2 ;
2017-12-25 22:47:57 +00:00
*/
hyperpoint onscreen ;
applymodel ( m - > h , onscreen ) ;
m - > x1 = ( 1 + onscreen [ 0 ] * vid . scale ) / 2 ;
2018-01-29 15:30:21 +00:00
m - > y1 = ( 1 - onscreen [ 1 ] * vid . scale ) / 2 ;
2017-12-27 09:52:54 +00:00
m - > valid = false ;
2017-12-25 22:47:57 +00:00
2017-12-27 10:59:37 +00:00
using namespace hyperpoint_vec ;
if ( sphere ) {
2017-12-27 09:52:54 +00:00
m - > valid = good_shape = true ;
2017-12-27 10:59:37 +00:00
ld scale ;
if ( gwhere = = gEuclid ) {
scale = modelscale ;
}
else if ( gwhere = = gNormal ) {
// sinh(scale) = modelscale
scale = asinh ( modelscale ) ;
}
2017-12-27 18:55:00 +00:00
else /* sphere/elliptic*/ {
2017-12-27 10:59:37 +00:00
if ( modelscale > = 1 )
// do as good as we can...
2018-01-30 23:11:49 +00:00
scale = M_PI / 2 - 1e-3 , good_shape = false , m - > valid = false ;
2017-12-27 10:59:37 +00:00
else scale = asin ( modelscale ) ;
}
m - > flat = h * scale ;
}
else if ( euclid & & gwhere = = gEuclid ) {
m - > flat = h * modelscale ;
2018-01-28 11:25:56 +00:00
m - > valid = good_shape = true ;
2017-12-27 10:59:37 +00:00
}
else if ( gwhere = = gNormal & & ( euclid | | ( hyperbolic & & modelscale > = 1 ) ) ) {
m - > valid = good_shape = true ;
ld d = hdist0 ( h ) ;
ld d0 = hypot2 ( h ) ; if ( ! d0 ) d0 = 1 ;
hyperpoint hpoint ;
bool orig_euclid = euclid ;
2017-12-27 18:55:00 +00:00
USING_NATIVE_GEOMETRY ;
2017-12-27 10:59:37 +00:00
if ( orig_euclid ) {
d * = modelscale ;
// point on a horocycle going through C0, in distance d along the horocycle
hpoint = hpxy ( d * d / 2 , d ) ;
}
else {
// radius of the equidistant
ld r = acosh ( modelscale ) ;
// point on an equdistant going through C0 in distance d along the guiding line
// hpoint = hpxy(cosh(r) * sinh(r) * (cosh(d) - 1), sinh(d) * cosh(r));
hpoint = xpush ( r ) * ypush ( d ) * xpush ( - r ) * C0 ;
2017-12-27 17:53:00 +00:00
hpoint [ 0 ] = - hpoint [ 0 ] ;
2017-12-27 10:59:37 +00:00
}
ld hpdist = hdist0 ( hpoint ) ;
ld z = hypot2 ( hpoint ) ;
2017-12-27 09:52:54 +00:00
if ( z = = 0 ) z = 1 ;
2017-12-27 10:59:37 +00:00
m - > flat = hpxyz ( hpdist * h [ 0 ] / d0 * hpoint [ 1 ] / z , hpdist * h [ 1 ] / d0 * hpoint [ 1 ] / z , - hpdist * hpoint [ 0 ] / z ) ;
2017-12-27 09:52:54 +00:00
}
else m - > flat = // hpxyz(h[0], h[1], sin(atan2(h[0], h[1]) * 3 + hyprand) * (h[2]-1) / 1000);
2017-12-27 18:55:00 +00:00
hpxyz ( h [ 0 ] , h [ 1 ] , ( h [ 2 ] - .99 ) * ( rand ( ) % 1000 - rand ( ) % 1000 ) / 1000 ) ;
if ( rug_perspective )
push_point ( m - > flat , 2 , - model_distance ) ;
2017-12-25 22:47:57 +00:00
2017-12-27 09:52:54 +00:00
// if(rug_perspective && gwhere == gEuclid) m->flat[2] -= 3;
2016-08-26 09:58:03 +00:00
m - > inqueue = false ;
2017-03-23 10:53:57 +00:00
m - > dist = dist ;
2016-08-26 09:58:03 +00:00
points . push_back ( m ) ;
return m ;
}
2017-12-27 05:31:47 +00:00
rugpoint * findRugpoint ( hyperpoint h ) {
2016-08-26 09:58:03 +00:00
for ( int i = 0 ; i < size ( points ) ; i + + )
2017-12-28 17:39:49 +00:00
if ( intvalxyz ( points [ i ] - > h , h ) < 1e-5 ) return points [ i ] ;
2017-12-27 05:31:47 +00:00
return NULL ;
}
rugpoint * findOrAddRugpoint ( hyperpoint h , double dist ) {
rugpoint * r = findRugpoint ( h ) ;
return r ? r : addRugpoint ( h , dist ) ;
2016-08-26 09:58:03 +00:00
}
2017-12-27 05:31:47 +00:00
void addNewEdge ( rugpoint * e1 , rugpoint * e2 , ld len = 1 ) {
2017-12-27 09:52:54 +00:00
edge e ; e . len = len ;
e . target = e2 ; e1 - > edges . push_back ( e ) ;
2016-08-26 09:58:03 +00:00
e . target = e1 ; e2 - > edges . push_back ( e ) ;
}
2017-12-27 05:31:47 +00:00
void addEdge ( rugpoint * e1 , rugpoint * e2 , ld len = 1 ) {
2016-08-26 09:58:03 +00:00
for ( int i = 0 ; i < size ( e1 - > edges ) ; i + + )
if ( e1 - > edges [ i ] . target = = e2 ) return ;
2017-12-27 05:31:47 +00:00
addNewEdge ( e1 , e2 , len ) ;
2016-08-26 09:58:03 +00:00
}
2017-12-27 05:31:47 +00:00
void addTriangle ( rugpoint * t1 , rugpoint * t2 , rugpoint * t3 , ld len = 1 ) {
addEdge ( t1 - > getglue ( ) , t2 - > getglue ( ) , len ) ;
addEdge ( t2 - > getglue ( ) , t3 - > getglue ( ) , len ) ;
addEdge ( t3 - > getglue ( ) , t1 - > getglue ( ) , len ) ;
2016-08-26 09:58:03 +00:00
triangles . push_back ( triangle ( t1 , t2 , t3 ) ) ;
}
2017-12-27 09:52:54 +00:00
map < pair < rugpoint * , rugpoint * > , rugpoint * > halves ;
rugpoint * findhalf ( rugpoint * r1 , rugpoint * r2 ) {
if ( r1 > r2 ) swap ( r1 , r2 ) ;
2018-01-05 16:18:37 +00:00
return halves [ make_pair ( r1 , r2 ) ] ;
2017-12-27 09:52:54 +00:00
}
2016-08-26 09:58:03 +00:00
void addTriangle1 ( rugpoint * t1 , rugpoint * t2 , rugpoint * t3 ) {
2017-12-27 09:52:54 +00:00
rugpoint * t12 = findhalf ( t1 , t2 ) ;
rugpoint * t23 = findhalf ( t2 , t3 ) ;
rugpoint * t31 = findhalf ( t3 , t1 ) ;
2016-08-26 09:58:03 +00:00
addTriangle ( t1 , t12 , t31 ) ;
addTriangle ( t12 , t2 , t23 ) ;
addTriangle ( t23 , t3 , t31 ) ;
addTriangle ( t23 , t31 , t12 ) ;
}
bool psort ( rugpoint * a , rugpoint * b ) {
2017-12-25 22:47:57 +00:00
return hdist0 ( a - > h ) < hdist0 ( b - > h ) ;
2016-08-26 09:58:03 +00:00
}
void calcLengths ( ) {
2017-12-25 22:47:57 +00:00
for ( int i = 0 ; i < size ( points ) ; i + + ) for ( int j = 0 ; j < size ( points [ i ] - > edges ) ; j + + ) {
ld d = hdist ( points [ i ] - > h , points [ i ] - > edges [ j ] . target - > h ) ;
if ( elliptic & & d > M_PI / 2 ) d = M_PI - d ;
points [ i ] - > edges [ j ] . len = d * modelscale ;
}
2016-08-26 09:58:03 +00:00
}
2017-11-06 18:24:02 +00:00
void setVidParam ( ) {
2017-12-25 22:47:57 +00:00
vid . xres = vid . yres = TEXTURESIZE ;
vid . scrsize = HTEXTURESIZE ;
vid . radius = vid . scrsize * vid . scale ; vid . xcenter = HTEXTURESIZE ; vid . ycenter = HTEXTURESIZE ;
2018-02-03 12:41:49 +00:00
vid . alpha = 1 ;
2017-11-06 18:24:02 +00:00
}
void buildTorusRug ( ) {
using namespace torusconfig ;
setVidParam ( ) ;
struct toruspoint {
int x , y ;
2017-12-28 17:39:49 +00:00
toruspoint ( ) { x = y = getqty ( ) ; }
2017-11-06 18:24:02 +00:00
toruspoint ( int _x , int _y ) : x ( _x ) , y ( _y ) { }
int d2 ( ) {
2017-12-25 22:47:57 +00:00
return x * x + ( euclid6 ? x * y : 0 ) + y * y ;
2017-11-06 18:24:02 +00:00
}
} ;
vector < toruspoint > zeropoints ;
vector < toruspoint > tps ( qty ) ;
2017-12-28 17:39:49 +00:00
auto & mode = tmodes [ torus_mode ] ;
bool single = mode . flags & TF_SINGLE ;
bool klein = mode . flags & TF_KLEIN ;
2017-11-06 18:24:02 +00:00
pair < toruspoint , toruspoint > solution ;
2017-12-28 17:39:49 +00:00
if ( single ) {
for ( int ax = - qty ; ax < qty ; ax + + )
for ( int ay = - qty ; ay < qty ; ay + + ) {
int v = ( ax * dx + ay * dy ) % qty ;
if ( v < 0 ) v + = qty ;
toruspoint tp ( ax , ay ) ;
if ( tps [ v ] . d2 ( ) > tp . d2 ( ) ) tps [ v ] = tp ;
if ( v = = 0 )
zeropoints . emplace_back ( ax , ay ) ;
}
ld bestsol = 1e12 ;
for ( auto p1 : zeropoints )
for ( auto p2 : zeropoints ) {
int det = p1 . x * p2 . y - p2 . x * p1 . y ;
if ( det < 0 ) continue ;
if ( det ! = qty & & det ! = - qty ) continue ;
ld quality = ld ( p1 . d2 ( ) ) * p2 . d2 ( ) ;
if ( quality < bestsol * 3 )
if ( quality < bestsol )
bestsol = quality , solution . first = p1 , solution . second = p2 ;
}
2017-11-06 18:24:02 +00:00
2017-12-28 17:39:49 +00:00
if ( solution . first . d2 ( ) > solution . second . d2 ( ) )
swap ( solution . first , solution . second ) ;
}
else {
if ( klein )
solution . first = toruspoint ( 2 * sdx , 0 ) ;
else
solution . first = toruspoint ( sdx , 0 ) ;
if ( mode . flags & TF_WEIRD )
solution . second = toruspoint ( sdy / 2 , sdy ) ;
else
solution . second = toruspoint ( 0 , sdy ) ;
if ( solution . first . d2 ( ) > solution . second . d2 ( ) )
swap ( solution . first , solution . second ) ;
2017-11-06 18:24:02 +00:00
}
ld factor = sqrt ( ld ( solution . second . d2 ( ) ) / solution . first . d2 ( ) ) ;
printf ( " factor = %lf \n " , factor ) ;
2017-12-28 17:39:49 +00:00
if ( factor < = 2.05 ) factor = 2.2 ;
2017-11-06 18:24:02 +00:00
factor - = 1 ;
// 22,1
// 7,-17
// transmatrix z1 = {{{22,7,0}, {1,-17,0}, {0,0,1}}};
transmatrix z1 = { { { ( ld ) solution . first . x , ( ld ) solution . second . x , 0 } , { ( ld ) solution . first . y , ( ld ) solution . second . y , 0 } , { 0 , 0 , 1 } } } ;
transmatrix z2 = inverse ( z1 ) ;
2017-12-28 17:39:49 +00:00
map < pair < int , int > , rugpoint * > glues ;
2017-11-07 13:39:26 +00:00
auto addToruspoint = [ & ] ( ld x , ld y ) {
2017-11-06 18:24:02 +00:00
auto r = addRugpoint ( C0 , 0 ) ;
hyperpoint onscreen ;
applymodel ( tC0 ( eumove ( x , y ) ) , onscreen ) ;
// take point (1,0)
// apply eumove(1,0)
// divide by EUCSCALE
// multiply by vid.radius (= HTEXTURESIZE * rugzoom)
// add 1, divide by texturesize
r - > x1 = onscreen [ 0 ] ;
r - > y1 = onscreen [ 1 ] ;
// r->y1 = (1 + onscreen[1] * rugzoom / EUCSCALE)/2;
hyperpoint h1 = hpxyz ( x , y , 0 ) ;
hyperpoint h2 = z2 * h1 ;
double alpha = - h2 [ 0 ] * 2 * M_PI ;
double beta = - h2 [ 1 ] * 2 * M_PI ;
// r->flat = {alpha, beta, 0};
double sc = ( factor + 1 ) / 4 ;
2017-12-28 17:39:49 +00:00
2017-12-27 09:52:54 +00:00
r - > flat = r - > h = hpxyz ( ( factor + cos ( alpha ) ) * cos ( beta ) * sc , ( factor + cos ( alpha ) ) * sin ( beta ) * sc , - sin ( alpha ) * sc ) ;
2017-11-06 18:24:02 +00:00
r - > valid = true ;
2017-12-28 17:39:49 +00:00
static const int X = 100003 ; // a prime
auto gluefun = [ ] ( ld z ) { return int ( frac ( z + .5 / X ) * X ) ; } ;
auto p = make_pair ( gluefun ( h2 [ 0 ] ) , gluefun ( h2 [ 1 ] ) ) ;
auto & r2 = glues [ p ] ;
if ( r2 ) r - > glueto ( r2 ) ; else r2 = r ;
2017-11-06 18:24:02 +00:00
return r ;
} ;
2017-12-27 09:52:54 +00:00
int rugmax = ( int ) sqrt ( vertex_limit / qty ) ;
if ( rugmax < 1 ) rugmax = 1 ;
if ( rugmax > 16 ) rugmax = 16 ;
2017-11-07 13:39:26 +00:00
ld rmd = rugmax ;
2017-12-28 17:39:49 +00:00
for ( int leaf = 0 ; leaf < ( klein ? 2 : 1 ) ; leaf + + )
for ( int i = 0 ; i < getqty ( ) ; i + + ) {
int x , y ;
if ( single ) {
x = tps [ i ] . x ;
y = tps [ i ] . y ;
}
else {
x = i % sdx ;
y = i / sdx ;
if ( x > sdx / 2 ) x - = sdx ;
if ( y > sdy / 2 ) y - = sdy ;
if ( leaf ) {
x + = sdx ;
if ( x > sdx ) x - = 2 * sdx ;
}
}
2017-11-06 18:24:02 +00:00
2017-11-07 13:39:26 +00:00
rugpoint * rugarr [ 32 ] [ 32 ] ;
for ( int yy = 0 ; yy < = rugmax ; yy + + )
for ( int xx = 0 ; xx < = rugmax ; xx + + )
2017-12-28 17:39:49 +00:00
rugarr [ yy ] [ xx ] = addToruspoint ( x + xx / rmd , y + ( yy - xx ) / rmd ) ;
2017-11-07 13:39:26 +00:00
for ( int yy = 0 ; yy < rugmax ; yy + + )
for ( int xx = 0 ; xx < rugmax ; xx + + )
2017-12-27 09:52:54 +00:00
addTriangle ( rugarr [ yy ] [ xx ] , rugarr [ yy + 1 ] [ xx ] , rugarr [ yy + 1 ] [ xx + 1 ] , modelscale / rugmax ) ,
addTriangle ( rugarr [ yy ] [ xx ] , rugarr [ yy ] [ xx + 1 ] , rugarr [ yy + 1 ] [ xx + 1 ] , modelscale / rugmax ) ;
2017-11-06 18:24:02 +00:00
}
double maxz = 0 ;
for ( auto p : points )
maxz = max ( maxz , max ( abs ( p - > x1 ) , abs ( p - > y1 ) ) ) ;
// maxz * rugzoom * vid.radius == vid.radius
2017-12-25 22:47:57 +00:00
vid . scale = 1 / maxz ;
2017-11-06 18:24:02 +00:00
for ( auto p : points )
2017-12-25 22:47:57 +00:00
p - > x1 = ( vid . xcenter + vid . radius * vid . scale * p - > x1 ) / vid . xres ,
p - > y1 = ( vid . ycenter - vid . radius * vid . scale * p - > y1 ) / vid . yres ;
2017-11-06 18:24:02 +00:00
2017-12-27 14:25:10 +00:00
qvalid = 0 ;
for ( auto p : points ) if ( ! p - > glue ) qvalid + + ;
2017-12-27 05:31:47 +00:00
printf ( " qvalid = %d \n " , qvalid ) ;
2017-12-27 18:55:00 +00:00
if ( rug_perspective )
push_all_points ( 2 , - model_distance ) ;
2017-12-27 05:31:47 +00:00
2017-11-06 18:24:02 +00:00
return ;
}
2017-12-27 10:59:37 +00:00
void verify ( ) {
vector < ld > ratios ;
for ( auto m : points )
for ( auto & e : m - > edges ) {
auto m2 = e . target ;
ld l = e . len ;
2017-12-27 20:08:31 +00:00
normalizer n ( m - > flat , m2 - > flat ) ;
hyperpoint h1 = n ( m - > flat ) ;
hyperpoint h2 = n ( m2 - > flat ) ;
2017-12-27 10:59:37 +00:00
ld l0 = hdist ( h1 , h2 ) ;
ratios . push_back ( l0 / l ) ;
}
printf ( " Length verification: \n " ) ;
sort ( ratios . begin ( ) , ratios . end ( ) ) ;
for ( int i = 0 ; i < size ( ratios ) ; i + = size ( ratios ) / 10 )
printf ( " %lf \n " , ratios [ i ] ) ;
printf ( " \n " ) ;
}
2017-12-29 11:54:50 +00:00
void comp ( cell * & minimum , cell * next ) {
int nc = next - > cpdist , mc = minimum - > cpdist ;
if ( tie ( nc , next ) < tie ( mc , minimum ) )
minimum = next ;
}
2016-08-26 09:58:03 +00:00
void buildRug ( ) {
2017-11-06 18:24:02 +00:00
if ( torus ) {
2017-12-27 09:52:54 +00:00
good_shape = true ;
2017-11-06 18:24:02 +00:00
buildTorusRug ( ) ;
return ;
}
2017-12-29 11:54:50 +00:00
celllister cl ( centerover . c ? centerover . c : cwt . c , sightrange , vertex_limit , NULL ) ;
2016-08-26 09:58:03 +00:00
map < cell * , rugpoint * > vptr ;
2017-12-29 11:54:50 +00:00
2018-01-29 15:30:44 +00:00
for ( int i = 0 ; i < size ( cl . lst ) ; i + + )
vptr [ cl . lst [ i ] ] = addRugpoint ( shmup : : ggmatrix ( cl . lst [ i ] ) * C0 , cl . dists [ i ] ) ;
2017-12-29 11:54:50 +00:00
for ( auto & p : vptr ) {
cell * c = p . first ;
rugpoint * v = p . second ;
for ( int j = 0 ; j < c - > type ; j + + ) try {
2016-08-26 09:58:03 +00:00
cell * c2 = c - > mov [ j ] ;
2017-12-29 11:54:50 +00:00
rugpoint * w = vptr . at ( c2 ) ;
2016-08-26 09:58:03 +00:00
// if(v<w) addEdge(v, w);
cell * c3 = c - > mov [ ( j + 1 ) % c - > type ] ;
2017-12-29 11:54:50 +00:00
rugpoint * w2 = vptr . at ( c3 ) ;
if ( a4 ) {
cellwalker cw ( c , j ) ;
cwstep ( cw ) ; cwspin ( cw , - 1 ) ; cwstep ( cw ) ;
cell * c4 = cw . c ;
cell * cm = c ; comp ( cm , c ) ; comp ( cm , c2 ) ; comp ( cm , c3 ) ; comp ( cm , c4 ) ;
if ( cm = = c | | cm = = c4 )
addTriangle ( v , w , w2 ) ;
}
else if ( v > w & & v > w2 )
addTriangle ( v , w , w2 ) ;
2016-08-26 09:58:03 +00:00
}
2017-12-29 11:54:50 +00:00
catch ( out_of_range ) { }
2016-08-26 09:58:03 +00:00
}
printf ( " vertices = %d triangles= %d \n " , size ( points ) , size ( triangles ) ) ;
calcLengths ( ) ;
sort ( points . begin ( ) , points . end ( ) , psort ) ;
2017-12-27 10:59:37 +00:00
verify ( ) ;
2018-01-30 23:11:49 +00:00
for ( auto p : points ) if ( p - > valid ) qvalid + + ;
2016-08-26 09:58:03 +00:00
}
// rug physics
queue < rugpoint * > pqueue ;
void enqueue ( rugpoint * m ) {
if ( m - > inqueue ) return ;
pqueue . push ( m ) ;
m - > inqueue = true ;
}
2017-12-27 09:52:54 +00:00
bool force_euclidean ( rugpoint & m1 , rugpoint & m2 , double rd , double d1 = 1 , double d2 = 1 ) {
if ( ! m1 . valid | | ! m2 . valid ) return false ;
2016-08-26 09:58:03 +00:00
// double rd = hdist(m1.h, m2.h) * xd;
// if(rd > rdz +1e-6 || rd< rdz-1e-6) printf("%lf %lf\n", rd, rdz);
double t = 0 ;
for ( int i = 0 ; i < 3 ; i + + ) t + = ( m1 . flat [ i ] - m2 . flat [ i ] ) * ( m1 . flat [ i ] - m2 . flat [ i ] ) ;
t = sqrt ( t ) ;
2017-12-27 09:52:54 +00:00
/* printf("%s ", display(m1.flat));
printf ( " %s " , display ( m2 . flat ) ) ;
printf ( " %lf/%lf \n " , t , rd ) ; */
current_total_error + = ( t - rd ) * ( t - rd ) ;
2017-12-27 05:31:47 +00:00
bool nonzero = abs ( t - rd ) > err_zero_current ;
2016-08-26 09:58:03 +00:00
double force = ( t - rd ) / t / 2 ; // 20.0;
for ( int i = 0 ; i < 3 ; i + + ) {
double di = ( m2 . flat [ i ] - m1 . flat [ i ] ) * force ;
m1 . flat [ i ] + = di * d1 ;
m2 . flat [ i ] - = di * d2 ;
if ( nonzero & & d2 > 0 ) enqueue ( & m2 ) ;
}
2017-12-27 09:52:54 +00:00
return nonzero ;
2016-08-26 09:58:03 +00:00
}
2017-12-27 09:52:54 +00:00
bool force ( rugpoint & m1 , rugpoint & m2 , double rd , double d1 = 1 , double d2 = 1 ) {
if ( ! m1 . valid | | ! m2 . valid ) return false ;
2017-12-25 22:47:57 +00:00
if ( gwhere = = gEuclid & & fast_euclidean ) {
2017-12-27 09:52:54 +00:00
return force_euclidean ( m1 , m2 , rd , d1 , d2 ) ;
2017-12-25 22:47:57 +00:00
}
// double rd = hdist(m1.h, m2.h) * xd;
// if(rd > rdz +1e-6 || rd< rdz-1e-6) printf("%lf %lf\n", rd, rdz);
using namespace hyperpoint_vec ;
2017-12-27 20:08:31 +00:00
normalizer n ( m1 . flat , m2 . flat ) ;
hyperpoint f1 = n ( m1 . flat ) ;
hyperpoint f2 = n ( m2 . flat ) ;
2017-12-25 22:47:57 +00:00
ld t = hdist ( f1 , f2 ) ;
2017-12-27 09:52:54 +00:00
current_total_error + = ( t - rd ) * ( t - rd ) ;
2017-12-27 05:31:47 +00:00
bool nonzero = abs ( t - rd ) > err_zero_current ;
2017-12-25 22:47:57 +00:00
double forcev = ( t - rd ) / 2 ; // 20.0;
transmatrix T = gpushxto0 ( f1 ) ;
transmatrix T1 = spintox ( T * f2 ) * T ;
transmatrix iT1 = inverse ( T1 ) ;
2018-01-05 16:18:37 +00:00
for ( int i = 0 ; i < 3 ; i + + ) if ( std : : isnan ( m1 . flat [ i ] ) ) {
2017-12-29 11:54:50 +00:00
addMessage ( " Failed! " ) ;
throw rug_exception ( ) ;
}
2017-12-25 22:47:57 +00:00
/*
printf ( " %p %p \n " , & m1 , & m2 ) ;
printf ( " m1 = %s \n " , display ( m1 . flat ) ) ;
printf ( " m2 = %s \n " , display ( m2 . flat ) ) ;
printf ( " Mi * m1 = %s \n " , display ( Mi * m1 . flat ) ) ;
printf ( " Mi * m2 = %s \n " , display ( Mi * m2 . flat ) ) ;
printf ( " f1 = %s \n " , display ( f1 ) ) ;
printf ( " T * f1 = %s \n " , display ( T * f1 ) ) ;
printf ( " T1 * f1 = %s \n " , display ( T1 * f1 ) ) ;
printf ( " f2 = %s \n " , display ( f2 ) ) ;
printf ( " T * f2 = %s \n " , display ( T * f2 ) ) ;
printf ( " T1 * f2 = %s \n " , display ( T1 * f2 ) ) ;
printf ( " iT1 = %s \n " , display ( iT1 * C0 ) ) ;
printf ( " iT1 + t = %s \n " , display ( iT1 * xpush ( t ) * C0 ) ) ;
*/
f1 = iT1 * xpush ( forcev ) * C0 ;
f2 = iT1 * xpush ( t - forcev ) * C0 ;
2017-12-27 20:08:31 +00:00
m1 . flat = n [ f1 ] ;
m2 . flat = n [ f2 ] ;
2017-12-25 22:47:57 +00:00
if ( nonzero & & d2 > 0 ) enqueue ( & m2 ) ;
2017-12-27 09:52:54 +00:00
return nonzero ;
2017-12-25 22:47:57 +00:00
}
2017-12-27 20:08:31 +00:00
vector < pair < ld , rugpoint * > > preset_points ;
2016-08-26 09:58:03 +00:00
void preset ( rugpoint * m ) {
int q = 0 ;
hyperpoint h ;
for ( int i = 0 ; i < 3 ; i + + ) h [ i ] = 0 ;
using namespace hyperpoint_vec ;
2017-12-27 20:08:31 +00:00
preset_points . clear ( ) ;
2016-08-26 09:58:03 +00:00
for ( int j = 0 ; j < size ( m - > edges ) ; j + + )
for ( int k = 0 ; k < j ; k + + ) {
rugpoint * a = m - > edges [ j ] . target ;
rugpoint * b = m - > edges [ k ] . target ;
if ( ! a - > valid ) continue ;
if ( ! b - > valid ) continue ;
double blen = - 1 ;
for ( int j2 = 0 ; j2 < size ( a - > edges ) ; j2 + + )
if ( a - > edges [ j2 ] . target = = b ) blen = a - > edges [ j2 ] . len ;
if ( blen < = 0 ) continue ;
for ( int j2 = 0 ; j2 < size ( a - > edges ) ; j2 + + )
for ( int k2 = 0 ; k2 < size ( b - > edges ) ; k2 + + )
if ( a - > edges [ j2 ] . target = = b - > edges [ k2 ] . target & & a - > edges [ j2 ] . target ! = m ) {
rugpoint * c = a - > edges [ j2 ] . target ;
if ( ! c - > valid ) continue ;
double a1 = m - > edges [ j ] . len / blen ;
double a2 = m - > edges [ k ] . len / blen ;
double c1 = a - > edges [ j2 ] . len / blen ;
double c2 = b - > edges [ k2 ] . len / blen ;
double cz = ( c1 * c1 - c2 * c2 + 1 ) / 2 ;
2017-12-29 11:54:50 +00:00
double ch = sqrt ( c1 * c1 - cz * cz + 1e-10 ) ;
2016-08-26 09:58:03 +00:00
double az = ( a1 * a1 - a2 * a2 + 1 ) / 2 ;
2017-12-29 11:54:50 +00:00
double ah = sqrt ( a1 * a1 - az * az + 1e-10 ) ;
2016-08-26 09:58:03 +00:00
// c->h = a->h + (b->h-a->h) * cz + ch * ort
hyperpoint ort = ( c - > flat - a - > flat - cz * ( b - > flat - a - > flat ) ) / ch ;
// m->h = a->h + (b->h-a->h) * az - ah * ort
hyperpoint res = a - > flat + ( b - > flat - a - > flat ) * az - ah * ort ;
2017-12-28 15:46:10 +00:00
h + = res ;
2017-03-23 10:53:57 +00:00
2017-12-27 20:08:31 +00:00
preset_points . emplace_back ( hypot ( blen * ( ah + ch ) , blen * ( az - cz ) ) , c ) ;
2017-03-23 10:53:57 +00:00
q + + ;
2017-12-28 15:46:10 +00:00
2017-12-29 13:35:18 +00:00
// printf("A %lf %lf %lf %lf C %lf %lf %lf %lf\n", a1, a2, az, ah, c1, c2, cz, ch);
2016-08-26 09:58:03 +00:00
}
}
2017-12-28 15:46:10 +00:00
if ( q > 0 ) m - > flat = h / q ;
2017-12-29 13:35:18 +00:00
// printf("preset (%d) -> %s\n", q, display(m->flat));
2018-01-05 18:05:41 +00:00
if ( std : : isnan ( m - > flat [ 0 ] ) | | std : : isnan ( m - > flat [ 1 ] ) | | std : : isnan ( m - > flat [ 2 ] ) )
2017-12-29 11:54:50 +00:00
throw rug_exception ( ) ;
2016-08-26 09:58:03 +00:00
}
2017-12-27 20:08:31 +00:00
ld sse ( hyperpoint h ) {
ld sse = 0 ;
for ( auto & p : preset_points ) {
ld l = p . first ;
normalizer n ( h , p . second - > flat ) ;
hyperpoint h1 = n ( h ) ;
hyperpoint h2 = n ( p . second - > flat ) ;
ld l0 = hdist ( h1 , h2 ) ;
sse + = ( l0 - l ) * ( l0 - l ) ;
}
return sse ;
}
void optimize ( rugpoint * m , bool do_preset ) {
if ( do_preset ) {
preset ( m ) ;
2017-12-30 14:12:15 +00:00
// int ed0 = size(preset_points);
2017-12-27 20:08:31 +00:00
for ( auto & e : m - > edges ) if ( e . target - > valid )
preset_points . emplace_back ( e . len , e . target ) ;
if ( gwhere > = gSphere ) {
ld cur = sse ( m - > flat ) ;
for ( int it = 0 ; it < 500 ; it + + ) {
ld ex = exp ( - it / 60 ) ;
again :
hyperpoint last = m - > flat ;
switch ( it % 6 ) {
case 0 : m - > flat [ 0 ] + = ex ; break ;
case 1 : m - > flat [ 0 ] - = ex ; break ;
case 2 : m - > flat [ 1 ] + = ex ; break ;
case 3 : m - > flat [ 1 ] - = ex ; break ;
case 4 : m - > flat [ 2 ] + = ex ; break ;
case 5 : m - > flat [ 2 ] - = ex ; break ;
}
ld now = sse ( m - > flat ) ;
if ( now < cur ) { cur = now ; ex * = 1.2 ; goto again ; }
else m - > flat = last ;
}
2017-12-29 13:35:18 +00:00
// printf("edges = [%d] %d sse = %lf\n",ed0, size(preset_points), cur);
2017-12-27 20:08:31 +00:00
}
}
for ( int it = 0 ; it < 50 ; it + + )
for ( int j = 0 ; j < size ( m - > edges ) ; j + + )
force ( * m , * m - > edges [ j ] . target , m - > edges [ j ] . len , 1 , 0 ) ;
}
2016-08-26 09:58:03 +00:00
int divides = 0 ;
bool stop = false ;
2017-12-27 09:52:54 +00:00
bool subdivide_further ( ) {
if ( torus ) return false ;
return size ( points ) * 4 < vertex_limit ;
}
2016-08-26 09:58:03 +00:00
void subdivide ( ) {
int N = size ( points ) ;
2017-12-25 22:47:57 +00:00
// if(euclid && gwhere == gEuclid) return;
2017-12-27 09:52:54 +00:00
if ( ! subdivide_further ( ) ) {
2017-12-27 10:59:37 +00:00
if ( euclid & & ! bounded & & gwhere = = gEuclid ) {
printf ( " Euclidean -- full precision \n " ) ;
stop = true ;
}
else {
err_zero_current / = 2 ;
printf ( " increasing precision to %lg \n " , err_zero_current ) ;
for ( auto p : points ) enqueue ( p ) ;
}
2017-12-27 05:31:47 +00:00
return ;
}
2016-08-26 09:58:03 +00:00
printf ( " subdivide (%d,%d) \n " , N , size ( triangles ) ) ;
divides + + ;
vector < triangle > otriangles = triangles ;
triangles . clear ( ) ;
2017-12-27 09:52:54 +00:00
halves . clear ( ) ;
2017-12-27 20:08:31 +00:00
2016-08-26 09:58:03 +00:00
// subdivide edges
for ( int i = 0 ; i < N ; i + + ) {
rugpoint * m = points [ i ] ;
for ( int j = 0 ; j < size ( m - > edges ) ; j + + ) {
rugpoint * m2 = m - > edges [ j ] . target ;
2017-12-27 09:52:54 +00:00
if ( m2 < m ) continue ;
2017-03-23 10:53:57 +00:00
rugpoint * mm = addRugpoint ( mid ( m - > h , m2 - > h ) , ( m - > dist + m2 - > dist ) / 2 ) ;
2018-01-05 18:05:41 +00:00
halves [ make_pair ( m , m2 ) ] = mm ;
2018-01-28 11:25:56 +00:00
if ( ! good_shape ) {
2017-12-29 13:35:18 +00:00
using namespace hyperpoint_vec ;
normalizer n ( m - > flat , m2 - > flat ) ;
hyperpoint h1 = n ( m - > flat ) ;
hyperpoint h2 = n ( m2 - > flat ) ;
mm - > flat = n [ mid ( h1 , h2 ) ] ;
}
2016-08-26 09:58:03 +00:00
mm - > valid = true ; qvalid + + ;
mm - > inqueue = false ; enqueue ( mm ) ;
}
m - > edges . clear ( ) ;
}
for ( int i = 0 ; i < size ( otriangles ) ; i + + )
addTriangle1 ( otriangles [ i ] . m [ 0 ] , otriangles [ i ] . m [ 1 ] , otriangles [ i ] . m [ 2 ] ) ;
calcLengths ( ) ;
printf ( " result (%d,%d) \n " , size ( points ) , size ( triangles ) ) ;
2017-12-27 20:08:31 +00:00
2016-08-26 09:58:03 +00:00
}
void addNewPoints ( ) {
2017-12-27 14:25:10 +00:00
if ( torus | | qvalid = = size ( points ) ) {
2016-08-26 09:58:03 +00:00
subdivide ( ) ;
return ;
}
2017-12-25 22:47:57 +00:00
double dist = hdist0 ( points [ qvalid ] - > h ) + .1e-6 ;
2016-08-26 09:58:03 +00:00
int oqvalid = qvalid ;
for ( int i = 0 ; i < size ( points ) ; i + + ) {
rugpoint & m = * points [ i ] ;
bool wasvalid = m . valid ;
2017-12-25 22:47:57 +00:00
m . valid = wasvalid | | sphere | | hdist0 ( m . h ) < = dist ;
2016-08-26 09:58:03 +00:00
if ( m . valid & & ! wasvalid ) {
qvalid + + ;
2017-12-27 20:08:31 +00:00
if ( ! good_shape ) optimize ( & m , i > 7 ) ;
2016-08-26 09:58:03 +00:00
enqueue ( & m ) ;
}
}
2017-12-27 09:52:54 +00:00
if ( qvalid ! = oqvalid ) { printf ( " adding new points %4d %4d %4d %.9lf %9d %9d \n " , oqvalid , qvalid , size ( points ) , dist , dt , queueiter ) ; }
2016-08-26 09:58:03 +00:00
}
2018-02-03 18:19:27 +00:00
# if !CAP_SDL
# include <stdlib.h>
# include <sys/time.h>
long long getVa ( ) {
struct timeval tval ;
gettimeofday ( & tval , NULL ) ;
return tval . tv_sec * 1000000 + tval . tv_usec ;
}
int SDL_GetTicks ( ) {
return getVa ( ) / 1000 ;
}
# endif
2016-08-26 09:58:03 +00:00
void physics ( ) {
2018-01-28 11:25:56 +00:00
if ( good_shape ) return ;
2017-12-27 09:52:54 +00:00
auto t = SDL_GetTicks ( ) ;
2017-12-25 22:47:57 +00:00
2017-12-27 09:52:54 +00:00
current_total_error = 0 ;
2017-12-25 22:47:57 +00:00
while ( SDL_GetTicks ( ) < t + 5 & & ! stop )
2017-12-27 10:59:37 +00:00
for ( int it = 0 ; it < 50 & & ! stop ; it + + )
2016-08-26 09:58:03 +00:00
if ( pqueue . empty ( ) ) addNewPoints ( ) ;
else {
queueiter + + ;
rugpoint * m = pqueue . front ( ) ;
pqueue . pop ( ) ;
m - > inqueue = false ;
2017-12-27 09:52:54 +00:00
bool moved = false ;
2016-08-26 09:58:03 +00:00
for ( int j = 0 ; j < size ( m - > edges ) ; j + + )
2017-12-27 09:52:54 +00:00
moved = force ( * m , * m - > edges [ j ] . target , m - > edges [ j ] . len ) | | moved ;
if ( moved ) enqueue ( m ) ;
2017-12-27 14:25:10 +00:00
}
2017-12-27 20:08:31 +00:00
2016-08-26 09:58:03 +00:00
}
// drawing the Rug
//-----------------
2018-02-03 12:41:49 +00:00
bool use_precompute ;
2016-08-26 09:58:03 +00:00
2017-12-27 05:31:47 +00:00
void getco ( rugpoint * m , hyperpoint & h , int & spherepoints ) {
using namespace hyperpoint_vec ;
2018-02-03 12:41:49 +00:00
h = use_precompute ? m - > getglue ( ) - > precompute : m - > getglue ( ) - > flat ;
2017-12-27 18:55:00 +00:00
if ( rug_perspective & & gwhere > = gSphere ) {
2017-12-27 13:12:27 +00:00
if ( h [ 2 ] > 0 ) {
ld rad = hypot3 ( h ) ;
// turn M_PI to -M_PI
2017-12-27 18:55:00 +00:00
// the only difference between sphere and elliptic is here:
// in elliptic, we subtract PI from the distance
ld rad_to = ( gwhere = = gSphere ? M_PI + M_PI : M_PI ) - rad ;
2017-12-27 13:12:27 +00:00
ld r = - rad_to / rad ;
h * = r ;
spherepoints + + ;
}
2017-12-25 22:47:57 +00:00
}
2016-08-26 09:58:03 +00:00
}
extern int besti ;
2018-01-30 23:16:16 +00:00
# if CAP_ODS
2018-01-28 11:20:21 +00:00
/* these functions are for the ODS projection, used in VR videos */
void cyclefix ( ld & a , ld b ) {
if ( a > b + M_PI ) a - = 2 * M_PI ;
if ( a < b - M_PI ) a + = 2 * M_PI ;
}
ld raddif ( ld a , ld b ) {
ld d = a - b ;
if ( d < 0 ) d = - d ;
if ( d > 2 * M_PI ) d - = 2 * M_PI ;
if ( d > M_PI ) d = 2 * M_PI - d ;
return d ;
}
bool ods = false ;
bool project_ods ( hyperpoint azeq , hyperpoint & h1 , hyperpoint & h2 , bool eye ) {
2018-01-29 15:31:14 +00:00
ld tanalpha = tan ( ipd / 2 ) ;
2018-01-28 11:20:21 +00:00
if ( eye ) tanalpha = - tanalpha ;
using namespace hyperpoint_vec ;
ld d = hypot3 ( azeq ) ;
ld sindbd = sin ( d ) / d , cosd = cos ( d ) ;
ld x = azeq [ 0 ] * sindbd ;
ld y = azeq [ 2 ] * sindbd ;
ld z = azeq [ 1 ] * sindbd ;
ld t = cosd ;
// printf("%10.5lf %10.5lf %10.5lf ", azeq[0], azeq[1], azeq[2]);
// printf(" => %10.5lf %10.5lf %10.5lf %10.5lf", x, y, z, t);
ld cottheta2 = ( x * x + y * y - tanalpha * tanalpha * t * t ) / ( z * z ) ;
// if(cottheta2 < 0) printf(" BAD\n");
if ( cottheta2 < 0 ) return false ;
ld theta = atan ( sqrt ( 1 / cottheta2 ) ) ;
for ( int i = 0 ; i < 2 ; i + + ) {
hyperpoint & h = ( i ? h1 : h2 ) ;
if ( i = = 1 ) theta = - theta ;
ld x0 = t * tanalpha ;
ld y0 = - z / tan ( theta ) ;
ld phi = atan2 ( y , x ) - atan2 ( y0 , x0 ) ;
2018-01-29 15:31:14 +00:00
ld delta = atan2 ( z / sin ( theta ) , t / cos ( ipd / 2 ) ) ;
2018-01-28 11:20:21 +00:00
h [ 0 ] = phi ;
h [ 1 ] = theta ;
h [ 2 ] = delta ;
// printf(" => %10.5lf %10.5lf %10.5lf", phi, theta, delta);
}
// printf("\n");
return true ;
}
2018-01-30 23:16:16 +00:00
# endif
2018-01-28 11:20:21 +00:00
2018-02-11 18:08:17 +00:00
vector < glhr : : ct_vertex > ct_array ;
2018-02-03 18:19:27 +00:00
2016-08-26 09:58:03 +00:00
void drawTriangle ( triangle & t ) {
2018-01-28 11:20:21 +00:00
using namespace hyperpoint_vec ;
2017-12-27 05:31:47 +00:00
for ( int i : { 0 , 1 , 2 } ) {
if ( ! t . m [ i ] - > valid ) return ;
if ( t . m [ i ] - > dist > = sightrange + .51 ) return ;
}
2016-08-26 09:58:03 +00:00
dt + + ;
2018-01-28 11:20:21 +00:00
2018-01-30 23:16:16 +00:00
# if CAP_ODS
2018-01-28 11:20:21 +00:00
if ( ods ) {
hyperpoint pts [ 3 ] ;
for ( int i = 0 ; i < 3 ; i + + )
pts [ i ] = t . m [ i ] - > getglue ( ) - > flat ;
hyperpoint hc = ( pts [ 1 ] - pts [ 0 ] ) ^ ( pts [ 2 ] - pts [ 0 ] ) ;
double hch = hypot3 ( hc ) ;
2018-02-09 02:40:46 +00:00
2018-01-28 11:20:21 +00:00
glNormal3f ( hc [ 0 ] / hch , hc [ 1 ] / hch , hc [ 2 ] / hch ) ;
bool ok = true ;
array < hyperpoint , 6 > h ;
for ( int eye = 0 ; eye < 2 ; eye + + ) {
if ( true ) {
for ( int i = 0 ; i < 3 ; i + + )
ok = ok & & project_ods ( pts [ i ] , h [ i ] , h [ i + 3 ] , eye ) ;
if ( ! ok ) return ;
for ( int i = 0 ; i < 6 ; i + + ) {
// let Delta be from 0 to 2PI
if ( h [ i ] [ 2 ] < 0 ) h [ i ] [ 2 ] + = 2 * M_PI ;
// Theta is from -PI/2 to PI/2. Let it be from 0 to PI
h [ i ] [ 1 ] + = ( eye ? - 1 : 1 ) * M_PI / 2 ;
}
}
else {
for ( int i = 0 ; i < 6 ; i + + )
h [ i ] [ 0 ] = - h [ i ] [ 0 ] ,
h [ i ] [ 1 ] = - h [ i ] [ 1 ] ,
h [ i ] [ 2 ] = 2 * M_PI - h [ i ] [ 2 ] ;
}
if ( raddif ( h [ 4 ] [ 0 ] , h [ 0 ] [ 0 ] ) < raddif ( h [ 1 ] [ 0 ] , h [ 0 ] [ 0 ] ) )
swap ( h [ 1 ] , h [ 4 ] ) ;
if ( abs ( h [ 1 ] [ 1 ] - h [ 0 ] [ 1 ] ) > M_PI / 2 ) return ;
if ( raddif ( h [ 5 ] [ 0 ] , h [ 0 ] [ 0 ] ) < raddif ( h [ 2 ] [ 0 ] , h [ 0 ] [ 0 ] ) )
swap ( h [ 5 ] , h [ 2 ] ) ;
if ( abs ( h [ 2 ] [ 1 ] - h [ 0 ] [ 1 ] ) > M_PI / 2 ) return ;
cyclefix ( h [ 1 ] [ 0 ] , h [ 0 ] [ 0 ] ) ;
cyclefix ( h [ 2 ] [ 0 ] , h [ 0 ] [ 0 ] ) ;
cyclefix ( h [ 4 ] [ 0 ] , h [ 3 ] [ 0 ] ) ;
cyclefix ( h [ 5 ] [ 0 ] , h [ 3 ] [ 0 ] ) ;
for ( int s : { 0 , 3 } ) {
int fst = 0 , lst = 0 ;
if ( h [ s + 1 ] [ 0 ] < - M_PI | | h [ s + 2 ] [ 0 ] < - M_PI ) lst + + ;
if ( h [ s + 1 ] [ 0 ] > + M_PI | | h [ s + 2 ] [ 0 ] > + M_PI ) fst - - ;
for ( int x = fst ; x < = lst ; x + + ) for ( int i = 0 ; i < 3 ; i + + ) {
glTexCoord2f ( t . m [ i ] - > x1 , t . m [ i ] - > y1 ) ;
glVertex3f ( h [ s + i ] [ 0 ] + 2 * M_PI * x , h [ s + i ] [ 1 ] , h [ s + i ] [ 2 ] ) ;
}
}
}
return ;
}
2018-01-30 23:16:16 +00:00
# endif
2017-12-25 22:47:57 +00:00
int spherepoints = 0 ;
2017-12-27 05:31:47 +00:00
array < hyperpoint , 3 > h ;
for ( int i : { 0 , 1 , 2 } ) getco ( t . m [ i ] , h [ i ] , spherepoints ) ;
2017-12-25 22:47:57 +00:00
if ( spherepoints = = 1 | | spherepoints = = 2 ) return ;
2016-08-26 09:58:03 +00:00
2017-12-27 05:31:47 +00:00
hyperpoint hc = ( h [ 1 ] - h [ 0 ] ) ^ ( h [ 2 ] - h [ 0 ] ) ;
double hch = hypot3 ( hc ) ;
2016-08-26 09:58:03 +00:00
2018-02-09 02:40:46 +00:00
ld col = ( 2 + hc [ 0 ] / hch ) / 3 ;
2018-02-11 18:08:17 +00:00
for ( int i : { 0 , 1 , 2 } )
ct_array . emplace_back ( h [ i ] , t . m [ i ] - > x1 , t . m [ i ] - > y1 , col ) ;
2016-08-26 09:58:03 +00:00
}
2018-02-01 12:42:47 +00:00
renderbuffer * glbuf ;
2016-08-26 09:58:03 +00:00
void prepareTexture ( ) {
2018-02-11 01:19:49 +00:00
resetbuffer rb ;
2016-08-26 09:58:03 +00:00
videopar svid = vid ;
setVidParam ( ) ;
2018-02-03 12:41:49 +00:00
dynamicval < stereo : : eStereo > d ( stereo : : mode , stereo : : sOFF ) ;
2016-08-26 09:58:03 +00:00
2018-02-01 12:42:47 +00:00
glbuf - > enable ( ) ;
2018-02-11 01:19:49 +00:00
stereo : : set_viewport ( 0 ) ;
stereo : : set_projection ( 0 ) ;
stereo : : set_mask ( 0 ) ;
2018-02-01 12:42:47 +00:00
glbuf - > clear ( 0 ) ;
2016-08-26 09:58:03 +00:00
2018-02-01 12:42:47 +00:00
ptds . clear ( ) ;
drawthemap ( ) ;
if ( mousing & & ! renderonce ) {
for ( int i = 0 ; i < numplayers ( ) ; i + + ) if ( multi : : playerActive ( i ) )
queueline ( tC0 ( shmup : : ggmatrix ( playerpos ( i ) ) ) , mouseh , 0xFF00FF , 8 ) ;
2016-08-26 09:58:03 +00:00
}
2018-02-01 12:42:47 +00:00
if ( finger_center ) {
transmatrix V = rgpushxto0 ( finger_center - > h ) ;
queuechr ( V , 0.5 , ' X ' , 0xFFFFFFFF , 2 ) ;
for ( int i = 0 ; i < 72 ; i + + )
queueline ( tC0 ( V * spin ( i * M_PI / 32 ) * xpush ( finger_range ) ) , tC0 ( V * spin ( ( i + 1 ) * M_PI / 32 ) * xpush ( finger_range ) ) , 0xFFFFFFFF , 0 ) ;
2016-08-26 09:58:03 +00:00
}
2018-02-01 12:42:47 +00:00
drawqueue ( ) ;
vid = svid ;
2018-02-11 01:19:49 +00:00
rb . reset ( ) ;
2016-08-26 09:58:03 +00:00
}
double xview , yview ;
2018-02-09 00:46:14 +00:00
void drawRugScene ( ) {
2018-02-01 12:42:47 +00:00
glbuf - > use_as_texture ( ) ;
2016-08-26 09:58:03 +00:00
2017-07-04 13:38:33 +00:00
if ( backcolor = = 0 )
glClearColor ( 0.05 , 0.05 , 0.05 , 1 ) ;
else
2018-02-09 00:46:14 +00:00
glhr : : colorClear ( backcolor < < 8 | 0xFF ) ;
2018-02-04 00:04:29 +00:00
# ifdef GLES_ONLY
2018-02-03 18:19:27 +00:00
glClearDepthf ( 1.0f ) ;
2018-02-04 00:04:29 +00:00
# else
glClearDepth ( 1.0f ) ;
# endif
2016-08-26 09:58:03 +00:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
glDisable ( GL_BLEND ) ;
2018-02-09 02:40:46 +00:00
glhr : : switch_mode ( glhr : : gmLightFog ) ;
2016-08-26 09:58:03 +00:00
glEnable ( GL_DEPTH_TEST ) ;
2017-03-23 10:53:57 +00:00
glDepthFunc ( GL_LESS ) ;
2016-08-26 09:58:03 +00:00
2018-02-03 12:41:49 +00:00
for ( int ed = stereo : : active ( ) & & stereo : : mode ! = stereo : : sODS ? - 1 : 0 ; ed < 2 ; ed + = 2 ) {
use_precompute = false ;
2018-02-11 18:08:17 +00:00
ct_array . clear ( ) ;
2018-02-03 12:41:49 +00:00
stereo : : set_mask ( ed ) , stereo : : set_viewport ( ed ) ;
if ( ed = = 1 & & stereo : : mode = = stereo : : sAnaglyph )
glClear ( GL_DEPTH_BUFFER_BIT ) ;
start_projection ( ed ) ;
2018-02-03 18:19:27 +00:00
if ( stereo : : mode = = stereo : : sODS ) {
2018-02-09 02:40:46 +00:00
glhr : : projection_multiply ( glhr : : ortho ( M_PI , M_PI , 2 * M_PI ) ) ;
2018-02-03 18:19:27 +00:00
}
2018-02-03 12:41:49 +00:00
else if ( rug_perspective | | stereo : : active ( ) ) {
xview = stereo : : tanfov ;
yview = stereo : : tanfov * vid . yres / vid . xres ;
2018-02-03 18:19:27 +00:00
2018-02-09 02:40:46 +00:00
glhr : : projection_multiply ( glhr : : frustum ( xview , yview , .01 , 100 ) ) ;
xview = - xview ; yview = - yview ;
2018-02-03 12:41:49 +00:00
2018-02-08 23:29:20 +00:00
if ( ! rug_perspective )
glhr : : projection_multiply ( glhr : : translate ( 0 , 0 , - model_distance ) ) ;
2018-02-03 12:41:49 +00:00
if ( ed ) {
if ( gwhere = = gEuclid )
2018-02-08 23:29:20 +00:00
glhr : : projection_multiply ( glhr : : translate ( stereo : : ipd * ed / 2 , 0 , 0 ) ) ;
2018-02-03 12:41:49 +00:00
else {
use_precompute = true ;
for ( auto p : points ) {
p - > precompute = p - > flat ;
push_point ( p - > precompute , 0 , stereo : : ipd * ed / 2 ) ;
}
}
}
}
else {
xview = stereo : : tanfov * model_distance ;
yview = stereo : : tanfov * model_distance * vid . yres / vid . xres ;
2018-02-03 18:19:27 +00:00
// glOrtho(-xview, xview, yview, -yview, -1000, 1000);
2018-02-09 02:40:46 +00:00
glhr : : projection_multiply ( glhr : : ortho ( xview , yview , - 1000 ) ) ;
2018-02-03 12:41:49 +00:00
}
2018-02-10 17:21:19 +00:00
glhr : : color2 ( 0xFFFFFFFF ) ;
2018-02-09 00:46:14 +00:00
2018-02-09 02:40:46 +00:00
glhr : : fog_max (
gwhere = = gSphere & & rug_perspective ? 10 :
gwhere = = gElliptic & & rug_perspective ? 4 :
2018-02-09 00:46:14 +00:00
100
) ;
2018-02-11 18:08:17 +00:00
2016-08-26 09:58:03 +00:00
for ( int t = 0 ; t < size ( triangles ) ; t + + )
drawTriangle ( triangles [ t ] ) ;
2018-02-09 00:46:14 +00:00
2018-02-11 18:08:17 +00:00
glhr : : set_modelview ( glhr : : id ( ) ) ;
glhr : : prepare ( ct_array ) ;
glDrawArrays ( GL_TRIANGLES , 0 , size ( ct_array ) ) ;
2018-02-03 12:41:49 +00:00
stereo : : set_mask ( 0 ) ;
2016-08-26 09:58:03 +00:00
}
glDisable ( GL_DEPTH_TEST ) ;
glEnable ( GL_BLEND ) ;
2018-02-03 12:41:49 +00:00
stereo : : set_mask ( 0 ) , stereo : : set_viewport ( 0 ) ;
stereo : : set_projection ( 0 ) ;
2017-12-27 13:12:27 +00:00
need_mouseh = true ;
2016-08-26 09:58:03 +00:00
}
// organization
//--------------
transmatrix currentrot ;
void init ( ) {
if ( rugged ) return ;
2018-02-03 13:35:06 +00:00
when_enabled = ticks ;
2018-02-11 01:19:49 +00:00
GLERR ( " before init " ) ;
2018-02-01 12:42:47 +00:00
glbuf = new renderbuffer ( TEXTURESIZE , TEXTURESIZE , vid . usingGL & & ! rendernogl ) ;
2018-02-03 12:50:47 +00:00
if ( ! glbuf - > valid ) {
addMessage ( XLAT ( " Failed to enable " ) ) ;
delete glbuf ;
return ;
}
rugged = true ;
2016-08-26 09:58:03 +00:00
if ( renderonce ) prepareTexture ( ) ;
if ( ! rugged ) return ;
genrug = true ;
drawthemap ( ) ;
genrug = false ;
qvalid = 0 ; dt = 0 ; queueiter = 0 ;
2017-12-27 09:52:54 +00:00
err_zero_current = err_zero ;
2016-08-26 09:58:03 +00:00
2017-12-29 11:54:50 +00:00
try {
buildRug ( ) ;
while ( good_shape & & subdivide_further ( ) ) subdivide ( ) ;
currentrot = Id ;
2017-12-29 13:31:18 +00:00
bool valid = true ;
for ( rugpoint * r : points )
if ( r - > x1 < 0 | | r - > x1 > 1 | | r - > y1 < 0 | | r - > y1 > 1 )
valid = false ;
if ( sphere & & pmodel = = mdDisk & & vid . alpha > 1 )
valid = false ;
if ( ! valid )
gotoHelp (
" Note: this mode is based on what you see on the screen -- but re-rendered in another way. "
2017-12-29 13:35:18 +00:00
" If not everything is shown on the screen (e.g., too zoomed in), the results will be incorrect "
" (though possibly interesting). "
2017-12-29 13:31:18 +00:00
" Use a different projection to fix this. "
) ;
2017-12-29 11:54:50 +00:00
}
catch ( rug_exception ) {
close ( ) ;
}
2016-08-26 09:58:03 +00:00
}
void close ( ) {
if ( ! rugged ) return ;
rugged = false ;
2018-02-01 12:42:47 +00:00
delete glbuf ;
2016-08-26 09:58:03 +00:00
triangles . clear ( ) ;
for ( int i = 0 ; i < size ( points ) ; i + + ) delete points [ i ] ;
points . clear ( ) ;
pqueue = queue < rugpoint * > ( ) ;
2018-01-29 15:30:21 +00:00
finger_center = NULL ;
2016-08-26 09:58:03 +00:00
}
int lastticks ;
2017-12-27 17:53:00 +00:00
ld protractor = 0 ;
2018-01-29 15:30:21 +00:00
void apply_rotation ( const transmatrix & t ) {
if ( ! rug_perspective ) currentrot = t * currentrot ;
for ( auto p : points ) p - > flat = t * p - > flat ;
}
2018-02-03 19:04:19 +00:00
void move_forward ( ld distance ) {
2018-02-04 00:04:29 +00:00
if ( rug_perspective ) push_all_points ( 2 , distance ) ;
2018-02-03 19:04:19 +00:00
else model_distance / = exp ( distance ) ;
}
2018-02-11 18:08:17 +00:00
# define CAP_HOLDKEYS CAP_SDL // && !ISWEB)
2018-01-29 15:30:21 +00:00
bool handlekeys ( int sym , int uni ) {
if ( uni = = ' 4 ' ) {
2018-01-30 23:16:16 +00:00
# if CAP_ODS
2018-01-29 15:30:21 +00:00
ods = ! ods ;
2018-01-30 23:16:16 +00:00
# endif
2018-01-29 15:30:21 +00:00
return true ;
}
else if ( uni = = ' 1 ' ) {
ld bdist = 1e12 ;
if ( finger_center )
finger_center = NULL ;
else {
for ( auto p : points ) {
ld cdist = hdist ( p - > getglue ( ) - > h , mouseh ) ;
if ( cdist < bdist )
bdist = cdist , finger_center = p - > getglue ( ) ;
}
}
return true ;
}
else if ( uni = = ' 2 ' ) {
apply_rotation ( rotmatrix ( M_PI , 0 , 2 ) ) ;
return true ;
}
else if ( uni = = ' 3 ' ) {
apply_rotation ( rotmatrix ( M_PI / 2 , 0 , 2 ) ) ;
return true ;
}
2018-02-11 18:08:17 +00:00
# if !CAP_HOLDKEYS
2018-02-04 00:04:29 +00:00
else if ( uni = = SDLK_PAGEUP | | uni = = ' [ ' ) {
move_forward ( .1 ) ;
return true ;
}
else if ( uni = = SDLK_PAGEDOWN | | uni = = ' ] ' ) {
move_forward ( - .1 ) ;
return true ;
}
2018-02-11 18:08:17 +00:00
else if ( uni = = SDLK_HOME ) { apply_rotation ( rotmatrix ( .1 , 0 , 1 ) ) ; return true ; }
else if ( uni = = SDLK_END ) { apply_rotation ( rotmatrix ( .1 , 1 , 0 ) ) ; return true ; }
else if ( uni = = SDLK_DOWN ) { apply_rotation ( rotmatrix ( .1 , 2 , 1 ) ) ; return true ; }
else if ( uni = = SDLK_UP ) { apply_rotation ( rotmatrix ( .1 , 1 , 2 ) ) ; return true ; }
else if ( uni = = SDLK_LEFT ) { apply_rotation ( rotmatrix ( .1 , 2 , 0 ) ) ; return true ; }
else if ( uni = = SDLK_RIGHT ) { apply_rotation ( rotmatrix ( .1 , 0 , 2 ) ) ; return true ; }
2018-02-03 13:31:17 +00:00
# endif
2018-01-29 15:30:21 +00:00
else return false ;
}
void finger_on ( int coord , ld val ) {
for ( auto p : points ) {
ld d = hdist ( finger_center - > h , p - > getglue ( ) - > h ) ;
push_point ( p - > flat , coord , val * finger_force * exp ( - sqr ( d / finger_range ) ) ) ;
}
enqueue ( finger_center ) , good_shape = false ;
}
2018-02-03 13:35:06 +00:00
transmatrix last_orientation ;
2016-08-26 09:58:03 +00:00
void actDraw ( ) {
2017-12-29 11:54:50 +00:00
try {
2018-02-11 01:19:49 +00:00
2016-08-26 09:58:03 +00:00
if ( ! renderonce ) prepareTexture ( ) ;
2018-02-11 01:19:49 +00:00
stereo : : set_viewport ( 0 ) ;
2016-08-26 09:58:03 +00:00
physics ( ) ;
drawRugScene ( ) ;
2018-02-03 13:31:17 +00:00
2018-02-03 13:35:06 +00:00
# if CAP_ORIENTATION
if ( ticks < when_enabled + 500 )
last_orientation = getOrientation ( ) ;
else {
transmatrix next_orientation = getOrientation ( ) ;
apply_rotation ( inverse ( last_orientation ) * next_orientation ) ;
last_orientation = next_orientation ;
2018-02-03 18:19:27 +00:00
}
2018-02-03 13:35:06 +00:00
# endif
2018-02-03 18:19:27 +00:00
2018-02-11 18:08:17 +00:00
# if CAP_HOLDKEYS
2016-08-26 09:58:03 +00:00
Uint8 * keystate = SDL_GetKeyState ( NULL ) ;
int qm = 0 ;
double alpha = ( ticks - lastticks ) / 1000.0 ;
lastticks = ticks ;
2017-12-25 22:47:57 +00:00
transmatrix t = Id ;
2018-01-29 15:30:21 +00:00
auto perform_finger = [ = ] ( ) {
if ( keystate [ SDLK_HOME ] ) finger_range / = exp ( alpha ) ;
if ( keystate [ SDLK_END ] ) finger_range * = exp ( alpha ) ;
if ( keystate [ SDLK_LEFT ] ) finger_on ( 0 , - alpha ) ;
if ( keystate [ SDLK_RIGHT ] ) finger_on ( 0 , alpha ) ;
if ( keystate [ SDLK_UP ] ) finger_on ( 1 , alpha ) ;
if ( keystate [ SDLK_DOWN ] ) finger_on ( 1 , - alpha ) ;
if ( keystate [ SDLK_PAGEDOWN ] ) finger_on ( 2 , - alpha ) ;
if ( keystate [ SDLK_PAGEUP ] ) finger_on ( 2 , + alpha ) ;
} ;
2017-12-25 22:47:57 +00:00
2018-01-29 15:30:21 +00:00
if ( cmode & sm : : NUMBER ) {
}
else if ( rug_perspective ) {
2017-12-25 22:47:57 +00:00
2018-01-29 15:30:21 +00:00
ld strafex = 0 , strafey = 0 , push = 0 ;
if ( finger_center )
perform_finger ( ) ;
else {
if ( keystate [ SDLK_HOME ] ) qm + + , t = t * rotmatrix ( alpha , 0 , 1 ) , protractor + = alpha ;
if ( keystate [ SDLK_END ] ) qm + + , t = t * rotmatrix ( alpha , 1 , 0 ) , protractor - = alpha ;
if ( ! keystate [ SDLK_LSHIFT ] ) {
if ( keystate [ SDLK_DOWN ] ) qm + + , t = t * rotmatrix ( alpha , 2 , 1 ) , protractor + = alpha ;
if ( keystate [ SDLK_UP ] ) qm + + , t = t * rotmatrix ( alpha , 1 , 2 ) , protractor - = alpha ;
if ( keystate [ SDLK_LEFT ] ) qm + + , t = t * rotmatrix ( alpha , 2 , 0 ) , protractor + = alpha ;
if ( keystate [ SDLK_RIGHT ] ) qm + + , t = t * rotmatrix ( alpha , 0 , 2 ) , protractor - = alpha ;
}
if ( keystate [ SDLK_PAGEDOWN ] ) push - = alpha ;
if ( keystate [ SDLK_PAGEUP ] ) push + = alpha ;
if ( keystate [ SDLK_LSHIFT ] ) {
if ( keystate [ SDLK_LEFT ] ) strafex + = alpha ;
if ( keystate [ SDLK_RIGHT ] ) strafex - = alpha ;
if ( keystate [ SDLK_UP ] ) strafey - = alpha ;
if ( keystate [ SDLK_DOWN ] ) strafey + = alpha ;
}
2017-12-25 22:47:57 +00:00
}
2016-08-26 09:58:03 +00:00
2017-12-27 18:10:34 +00:00
if ( qm ) {
if ( keystate [ SDLK_LCTRL ] )
push_all_points ( 2 , + model_distance ) ;
2018-01-29 15:30:21 +00:00
apply_rotation ( t ) ;
2017-12-27 18:10:34 +00:00
if ( keystate [ SDLK_LCTRL ] )
push_all_points ( 2 , - model_distance ) ;
2017-12-25 22:47:57 +00:00
}
2017-12-27 17:53:00 +00:00
model_distance - = push ;
2017-12-25 22:47:57 +00:00
push_all_points ( 2 , push ) ;
push_all_points ( 0 , strafex ) ;
push_all_points ( 1 , strafey ) ;
}
else {
2018-01-29 15:30:21 +00:00
if ( finger_center )
perform_finger ( ) ;
else {
if ( keystate [ SDLK_HOME ] ) qm + + , t = inverse ( currentrot ) ;
if ( keystate [ SDLK_END ] ) qm + + , t = currentrot * rotmatrix ( alpha , 0 , 1 ) * inverse ( currentrot ) ;
if ( keystate [ SDLK_DOWN ] ) qm + + , t = t * rotmatrix ( alpha , 1 , 2 ) ;
if ( keystate [ SDLK_UP ] ) qm + + , t = t * rotmatrix ( alpha , 2 , 1 ) ;
if ( keystate [ SDLK_LEFT ] ) qm + + , t = t * rotmatrix ( alpha , 0 , 2 ) ;
if ( keystate [ SDLK_RIGHT ] ) qm + + , t = t * rotmatrix ( alpha , 2 , 0 ) ;
if ( keystate [ SDLK_PAGEUP ] ) model_distance / = exp ( alpha ) ;
if ( keystate [ SDLK_PAGEDOWN ] ) model_distance * = exp ( alpha ) ;
2017-12-25 22:47:57 +00:00
}
2018-01-29 15:30:21 +00:00
if ( qm ) apply_rotation ( t ) ;
2016-08-26 09:58:03 +00:00
}
2018-02-03 13:31:17 +00:00
# endif
2017-12-29 11:54:50 +00:00
}
catch ( rug_exception ) {
rug : : close ( ) ;
}
2016-08-26 09:58:03 +00:00
}
int besti ;
2017-12-27 13:12:27 +00:00
void getco_pers ( rugpoint * r , hyperpoint & p , int & spherepoints , bool & error ) {
getco ( r , p , spherepoints ) ;
2017-12-27 12:09:58 +00:00
if ( rug_perspective ) {
if ( p [ 2 ] > = 0 )
error = true ;
else {
p [ 0 ] / = p [ 2 ] ;
p [ 1 ] / = p [ 2 ] ;
}
}
}
2017-12-27 14:25:10 +00:00
static const ld RADAR_INF = 1e12 ;
ld radar_distance = RADAR_INF ;
2016-08-26 09:58:03 +00:00
hyperpoint gethyper ( ld x , ld y ) {
2018-02-09 02:40:46 +00:00
double mx = ( x - vid . xcenter ) / vid . xres * 2 * xview ;
double my = ( vid . ycenter - y ) / vid . yres * 2 * yview ;
2017-12-27 14:25:10 +00:00
radar_distance = RADAR_INF ;
2016-08-26 09:58:03 +00:00
2017-11-07 15:16:04 +00:00
double rx1 = 0 , ry1 = 0 ;
2017-11-06 18:24:02 +00:00
bool found = false ;
for ( int i = 0 ; i < size ( triangles ) ; i + + ) {
auto r0 = triangles [ i ] . m [ 0 ] ;
auto r1 = triangles [ i ] . m [ 1 ] ;
auto r2 = triangles [ i ] . m [ 2 ] ;
2017-12-27 12:09:58 +00:00
hyperpoint p0 , p1 , p2 ;
bool error = false ;
2017-12-27 13:12:27 +00:00
int spherepoints = 0 ;
getco_pers ( r0 , p0 , spherepoints , error ) ;
getco_pers ( r1 , p1 , spherepoints , error ) ;
getco_pers ( r2 , p2 , spherepoints , error ) ;
if ( error | | spherepoints = = 1 | | spherepoints = = 2 ) continue ;
2017-12-27 12:09:58 +00:00
double dx1 = p1 [ 0 ] - p0 [ 0 ] ;
double dy1 = p1 [ 1 ] - p0 [ 1 ] ;
double dx2 = p2 [ 0 ] - p0 [ 0 ] ;
double dy2 = p2 [ 1 ] - p0 [ 1 ] ;
double dxm = mx - p0 [ 0 ] ;
double dym = my - p0 [ 1 ] ;
2017-11-06 18:24:02 +00:00
// A (dx1,dy1) = (1,0)
// B (dx2,dy2) = (0,1)
double det = dx1 * dy2 - dy1 * dx2 ;
double tx = dxm * dy2 - dym * dx2 ;
double ty = - ( dxm * dy1 - dym * dx1 ) ;
tx / = det ; ty / = det ;
if ( tx > = 0 & & ty > = 0 & & tx + ty < = 1 ) {
2017-12-27 12:09:58 +00:00
double rz1 = p0 [ 2 ] * ( 1 - tx - ty ) + p1 [ 2 ] * tx + p2 [ 2 ] * ty ;
2017-12-27 18:10:34 +00:00
rz1 = - rz1 ; if ( ! rug_perspective ) rz1 + = model_distance ;
2017-12-27 14:25:10 +00:00
if ( rz1 < radar_distance ) {
radar_distance = rz1 ;
2017-11-06 18:24:02 +00:00
rx1 = r0 - > x1 + ( r1 - > x1 - r0 - > x1 ) * tx + ( r2 - > x1 - r0 - > x1 ) * ty ;
ry1 = r0 - > y1 + ( r1 - > y1 - r0 - > y1 ) * tx + ( r2 - > y1 - r0 - > y1 ) * ty ;
}
found = true ;
}
2016-08-26 09:58:03 +00:00
}
2017-11-07 13:17:13 +00:00
if ( ! found ) return Hypc ;
2017-11-06 18:24:02 +00:00
double px = rx1 * TEXTURESIZE , py = ( 1 - ry1 ) * TEXTURESIZE ;
2016-08-26 09:58:03 +00:00
videopar svid = vid ;
setVidParam ( ) ;
hyperpoint h = : : gethyper ( px , py ) ;
vid = svid ;
return h ;
}
void show ( ) {
2018-02-11 01:19:49 +00:00
cmode = sm : : SIDE ;
2017-12-27 14:25:10 +00:00
gamescreen ( 0 ) ;
2017-03-23 10:53:57 +00:00
dialog : : init ( XLAT ( " hypersian rug mode " ) , iinf [ itPalace ] . color , 150 , 100 ) ;
2017-10-30 09:01:49 +00:00
2017-03-23 10:53:57 +00:00
dialog : : addItem ( XLAT ( " what's this? " ) , ' h ' ) ;
dialog : : addItem ( XLAT ( " take me back " ) , ' q ' ) ;
2017-10-30 09:01:49 +00:00
2017-12-27 14:25:10 +00:00
dialog : : addBoolItem ( XLAT ( " enable the Hypersian Rug mode " ) , rug : : rugged , ' u ' ) ;
2017-03-23 10:53:57 +00:00
dialog : : addBoolItem ( XLAT ( " render the texture only once " ) , ( renderonce ) , ' o ' ) ;
2018-02-03 12:54:51 +00:00
# if CAP_SDL
2017-12-27 14:25:10 +00:00
dialog : : addBoolItem ( XLAT ( " render texture without OpenGL " ) , ( rendernogl ) , ' g ' ) ;
2018-02-03 12:54:51 +00:00
# else
rendernogl = false ;
# endif
2017-03-23 10:53:57 +00:00
dialog : : addSelItem ( XLAT ( " texture size " ) , its ( texturesize ) + " x " + its ( texturesize ) , ' s ' ) ;
2017-12-27 14:25:10 +00:00
dialog : : addSelItem ( XLAT ( " vertex limit " ) , its ( vertex_limit ) , ' v ' ) ;
if ( rug : : rugged )
dialog : : lastItem ( ) . value + = " ( " + its ( qvalid ) + " ) " ;
2017-12-27 17:53:00 +00:00
dialog : : addSelItem ( XLAT ( " model distance " ) , fts ( model_distance ) , ' d ' ) ;
2017-12-27 14:25:10 +00:00
dialog : : addBoolItem ( XLAT ( " projection " ) , rug_perspective , ' p ' ) ;
2017-12-27 17:53:00 +00:00
dialog : : lastItem ( ) . value = XLAT ( rug_perspective ? " perspective " :
gwhere = = gEuclid ? " orthogonal " : " azimuthal equidistant " ) ;
2017-12-27 14:25:10 +00:00
if ( ! rug : : rugged )
2017-12-27 17:53:00 +00:00
dialog : : addSelItem ( XLAT ( " native geometry " ) , XLAT ( gwhere ? ginf [ gwhere ] . name : " hyperbolic " ) , ' n ' ) ;
2017-12-27 14:25:10 +00:00
else
dialog : : addSelItem ( XLAT ( " radar " ) , radar_distance = = RADAR_INF ? " ∞ " : fts4 ( radar_distance ) , ' r ' ) ;
2018-01-28 11:21:10 +00:00
dialog : : addSelItem ( XLAT ( " model scale factor " ) , fts ( modelscale ) , ' m ' ) ;
if ( rug : : rugged )
2017-12-27 14:25:10 +00:00
dialog : : addSelItem ( XLAT ( " model iterations " ) , its ( queueiter ) , 0 ) ;
2018-02-03 12:41:49 +00:00
dialog : : addItem ( XLAT ( " stereo vision config " ) , ' f ' ) ;
2017-12-27 17:53:00 +00:00
// dialog::addSelItem(XLAT("protractor"), fts(protractor * 180 / M_PI) + "°", 'f');
2018-01-28 11:25:56 +00:00
if ( ! good_shape ) {
2018-01-03 10:22:37 +00:00
dialog : : addSelItem ( XLAT ( " maximum error " ) , ftsg ( err_zero ) , ' e ' ) ;
2017-12-27 14:25:10 +00:00
if ( rug : : rugged )
dialog : : lastItem ( ) . value + = " ( " + ftsg ( err_zero_current ) + " ) " ;
2017-11-07 13:39:26 +00:00
}
2017-12-27 14:25:10 +00:00
2017-03-23 10:53:57 +00:00
dialog : : display ( ) ;
2017-07-10 18:47:38 +00:00
keyhandler = [ ] ( int sym , int uni ) {
dialog : : handleNavigation ( sym , uni ) ;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if ( uni = = ' h ' ) gotoHelp (
2018-01-03 01:04:34 +00:00
XLAT (
2016-08-26 09:58:03 +00:00
" In this mode, HyperRogue is played on a 3D model of a part of the hyperbolic plane, "
" similar to one you get from the 'paper model creator' or by hyperbolic crocheting. \n \n "
" This requires some OpenGL extensions and may crash or not work correctly -- enabling "
" the 'render texture without OpenGL' options may be helpful in this case. Also the 'render once' option "
" will make the rendering faster, but the surface will be rendered only once, so "
" you won't be able to play a game on it. \n \n "
2018-01-03 01:04:34 +00:00
" Use arrow keys to rotate, Page Up/Down to zoom. " )
+ " \n \n " +
XLAT ( " In the perspective projection, you can use arrows to rotate the camera, Page Up/Down to go forward/backward, Shift+arrows to strafe, and Ctrl+arrows to rotate the model. " )
2017-07-10 18:47:38 +00:00
) ;
else if ( uni = = ' u ' ) {
2017-12-27 14:25:10 +00:00
if ( rug : : rugged ) rug : : close ( ) ;
else rug : : init ( ) ;
2017-07-10 18:47:38 +00:00
}
2018-01-30 23:16:16 +00:00
# if CAP_ODS
2018-01-29 15:30:21 +00:00
else if ( uni = = ' I ' )
dialog : : editNumber ( ipd , 0 , 1 , .002 , .05 , " interpupilar distance " ,
" Used in the ODS projection. "
) ;
2018-01-30 23:16:16 +00:00
# endif
2018-01-29 15:30:21 +00:00
else if ( uni = = ' R ' )
dialog : : editNumber ( finger_range , 0 , 1 , .01 , .1 , " finger range " ,
" Press 1 to enable the finger mode. "
) ;
else if ( uni = = ' F ' )
dialog : : editNumber ( finger_force , 0 , 1 , .01 , .1 , " finger force " ,
" Press 1 to enable the finger force. "
) ;
2017-12-27 14:25:10 +00:00
else if ( uni = = ' o ' & & ! rug : : rugged )
2017-07-10 18:47:38 +00:00
renderonce = ! renderonce ;
2017-12-27 14:25:10 +00:00
else if ( uni = = ' v ' ) {
2018-01-03 01:04:34 +00:00
dialog : : editNumber ( vertex_limit , 0 , 50000 , 500 , 3000 , " vertex limit " ,
" The more vertices, the more accurate the Hypersian Rug model is. "
2018-01-04 19:05:34 +00:00
" However, a number too high might make the model slow to compute and render. "
2018-01-03 01:04:34 +00:00
) ;
2017-12-27 14:25:10 +00:00
dialog : : reaction = [ ] ( ) { err_zero_current = err_zero ; } ;
}
else if ( uni = = ' r ' )
2017-12-27 18:10:34 +00:00
addMessage ( XLAT ( " This just shows the 'z' coordinate of the selected point. " ) ) ;
2017-12-27 14:25:10 +00:00
else if ( uni = = ' m ' ) {
2018-01-28 11:21:10 +00:00
dialog : : editNumber ( modelscale , 0.1 , 10 , rugged ? .001 : .1 , 1 , " model scale factor " ,
2018-01-04 19:05:34 +00:00
" This is relevant when the native geometry is not Euclidean. "
2017-12-27 14:25:10 +00:00
" For example, if the native geometry is spherical, and scale < 1, a 2d sphere will be rendered as a subsphere; "
" if the native geometry is hyperbolic, and scale > 1, a hyperbolic plane will be rendered as an equidistant surface. "
) ;
dialog : : scaleLog ( ) ;
2018-01-28 11:21:10 +00:00
if ( rug : : rugged ) {
static ld last ;
last = modelscale ;
dialog : : reaction = [ ] ( ) {
for ( auto p : points ) {
2018-01-29 15:31:28 +00:00
for ( auto & e : p - > edges ) e . len * = modelscale / last ;
2018-01-28 11:21:10 +00:00
enqueue ( p ) ;
}
last = modelscale ;
2018-01-28 11:25:56 +00:00
good_shape = false ;
2018-01-29 15:31:28 +00:00
} ;
2018-01-28 11:21:10 +00:00
}
2017-12-27 14:25:10 +00:00
}
2017-12-27 17:53:00 +00:00
else if ( uni = = ' p ' ) {
2017-12-27 14:25:10 +00:00
rug_perspective = ! rug_perspective ;
2017-12-27 17:53:00 +00:00
if ( rugged ) {
if ( rug_perspective )
push_all_points ( 2 , - model_distance ) ;
else
push_all_points ( 2 , + model_distance ) ;
}
}
else if ( uni = = ' d ' )
2018-01-03 01:04:34 +00:00
dialog : : editNumber ( model_distance , - 10 , 10 , .1 , 1 , " model distance " ,
" In the perspective projection, this sets the distance from the camera to the center of the model. "
" In the orthogonal projection this just controls the scale. "
) ;
2017-12-27 14:25:10 +00:00
else if ( uni = = ' e ' ) {
2018-01-03 10:22:37 +00:00
dialog : : editNumber ( err_zero , 1e-9 , 1 , .1 , 1e-3 , " maximum error " ,
" New points are added when the current error in the model is smaller than this value. "
) ;
2017-12-27 14:25:10 +00:00
dialog : : scaleLog ( ) ;
dialog : : reaction = [ ] ( ) { err_zero_current = err_zero ; } ;
}
2018-02-03 12:41:49 +00:00
else if ( uni = = ' f ' )
pushScreen ( showStereo ) ;
2017-12-27 17:53:00 +00:00
else if ( uni = = ' n ' & & ! rug : : rugged )
gwhere = eGeometry ( ( gwhere + 1 ) % 4 ) ;
2018-02-03 12:54:51 +00:00
else if ( uni = = ' g ' & & ! rug : : rugged & & CAP_SDL )
2017-07-10 18:47:38 +00:00
rendernogl = ! rendernogl ;
2017-12-27 14:25:10 +00:00
else if ( uni = = ' s ' & & ! rug : : rugged ) {
2017-07-10 18:47:38 +00:00
texturesize * = 2 ;
2018-02-03 12:50:47 +00:00
if ( texturesize = = 8192 ) texturesize = 64 ;
2017-07-10 18:47:38 +00:00
}
2018-01-29 15:30:21 +00:00
else if ( handlekeys ( sym , uni ) ) ;
2017-07-10 18:47:38 +00:00
else if ( doexiton ( sym , uni ) ) popScreen ( ) ;
} ;
2016-08-26 09:58:03 +00:00
}
void select ( ) {
2017-12-27 14:25:10 +00:00
pushScreen ( rug : : show ) ;
2016-08-26 09:58:03 +00:00
}
2017-12-25 22:47:57 +00:00
2018-02-03 18:19:27 +00:00
# if CAP_COMMANDLINE
2017-12-25 22:47:57 +00:00
int rugArgs ( ) {
using namespace arg ;
if ( 0 ) ;
else if ( argis ( " -rugmodelscale " ) ) {
shift ( ) ; modelscale = argf ( ) ;
}
else if ( argis ( " -ruggeo " ) ) {
shift ( ) ; gwhere = ( eGeometry ) argi ( ) ;
}
else if ( argis ( " -rugpers " ) ) {
rug_perspective = true ;
}
else if ( argis ( " -rugorth " ) ) {
rug_perspective = false ;
}
else if ( argis ( " -rugerr " ) ) {
2017-12-27 09:52:54 +00:00
shift ( ) ; err_zero = argf ( ) ;
}
2018-01-28 11:21:29 +00:00
else if ( argis ( " -rugtsize " ) ) {
shift ( ) ; rug : : texturesize = argi ( ) ;
}
2017-12-27 09:52:54 +00:00
else if ( argis ( " -rugv " ) ) {
shift ( ) ; vertex_limit = argi ( ) ;
2017-12-25 22:47:57 +00:00
}
2018-02-03 12:41:49 +00:00
else if ( argis ( " -rugon " ) ) {
PHASE ( 3 ) ; rug : : init ( ) ;
}
2018-01-30 23:16:16 +00:00
# if CAP_ODS
2018-01-28 11:20:21 +00:00
else if ( argis ( " -ods " ) ) {
ods = true ;
2018-01-29 15:31:14 +00:00
}
else if ( argis ( " -ipd " ) ) {
2018-01-28 11:20:21 +00:00
shift ( ) ; ipd = argf ( ) ;
}
2018-01-30 23:16:16 +00:00
# endif
2018-01-28 11:20:21 +00:00
2017-12-25 22:47:57 +00:00
else return 1 ;
return 0 ;
}
auto rug_hook =
addHook ( hooks_args , 100 , rugArgs ) ;
2018-02-03 18:19:27 +00:00
# endif
2016-08-26 09:58:03 +00:00
}
# else
// fake for mobile
namespace rug {
bool rugged = false ;
bool renderonce = false ;
bool rendernogl = true ;
int texturesize = 512 ;
2017-07-24 22:21:36 +00:00
ld scale = 1.0f ;
2016-08-26 09:58:03 +00:00
}
# endif