2019-08-10 11:43:24 +00:00
// Hyperbolic Rogue - Hypersian Rug mode
2016-08-26 09:58:03 +00:00
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
2019-08-10 11:43:24 +00:00
/** \file rug.cpp
* \ brief Hypersian Rug mode
*
* See also surface . cpp for constant curvature surfaces .
*/
2016-08-26 09:58:03 +00:00
2019-09-05 07:15:40 +00:00
# include "hyper.h"
2018-06-10 23:58:31 +00:00
namespace hr {
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)
2019-08-09 23:07:39 +00:00
EX bool rug_failure = false ;
2018-03-02 12:06:28 +00:00
2019-08-09 23:07:39 +00:00
EX namespace rug {
2016-08-26 09:58:03 +00:00
2019-08-09 23:07:39 +00:00
EX ld lwidth = 2 ;
2019-03-30 16:46:50 +00:00
2019-08-09 23:07:39 +00:00
EX bool in_crystal ( ) { return surface : : sh = = surface : : dsCrystal ; }
2018-11-30 19:29:14 +00:00
2018-03-24 14:17:17 +00:00
bool computed = false ;
2019-08-09 23:07:39 +00:00
# if HDR
struct edge {
struct rugpoint * target ;
ld len ;
} ;
struct dexp_data {
hyperpoint params ;
hyperpoint cont ;
ld remaining_distance ;
} ;
struct rugpoint {
double x1 , y1 ;
bool valid ;
bool inqueue ;
double dist ;
hyperpoint h ; // point in the represented space
hyperpoint flat ; // point in the native space, in azeq
hyperpoint precompute ;
vector < edge > edges ;
vector < edge > anticusp_edges ;
// Find-Union algorithm
rugpoint * glue ;
rugpoint * getglue ( ) {
return glue ? ( glue = glue - > getglue ( ) ) : this ;
}
hyperpoint & glueflat ( ) {
return glue - > flat ;
}
rugpoint ( ) { glue = NULL ; }
void glueto ( rugpoint * x ) {
x = x - > getglue ( ) ;
auto y = getglue ( ) ;
if ( x ! = y ) y - > glue = x ;
}
int dexp_id ;
dexp_data surface_point ;
} ;
struct triangle {
rugpoint * m [ 3 ] ;
triangle ( rugpoint * m1 , rugpoint * m2 , rugpoint * m3 ) {
m [ 0 ] = m1 ; m [ 1 ] = m2 ; m [ 2 ] = m3 ;
}
} ;
# endif
EX vector < rugpoint * > points ;
EX vector < triangle > triangles ;
2018-03-24 14:17:17 +00:00
2018-02-03 13:35:06 +00:00
int when_enabled ;
2017-12-29 11:54:50 +00:00
struct rug_exception { } ;
2019-08-09 23:07:39 +00:00
EX bool fast_euclidean = true ;
EX bool good_shape ;
EX bool subdivide_first = false ;
EX bool spatial_rug = false ;
2018-02-27 18:27:20 +00:00
2019-08-09 23:07:39 +00:00
EX bool subdivide_further ( ) ;
EX void subdivide ( ) ;
2017-12-25 22:47:57 +00:00
2019-08-09 23:07:39 +00:00
EX ld modelscale = 1 ;
EX ld model_distance = 4 ;
2017-11-06 18:24:02 +00:00
2019-08-09 23:07:39 +00:00
EX eGeometry gwhere = gEuclid ;
2017-12-25 22:47:57 +00:00
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
//-------------------------------------
2019-08-09 23:07:39 +00:00
EX bool rugged = false ;
2016-08-26 09:58:03 +00:00
bool genrug = false ;
2019-08-09 23:07:39 +00:00
EX int vertex_limit = 20000 ;
2017-12-27 09:52:54 +00:00
2019-08-09 23:07:39 +00:00
EX bool renderonce = false ;
EX int renderlate = 0 ;
EX bool rendernogl = false ;
EX int texturesize = 1024 ;
EX ld scale = 1 ;
EX ld ruggo = 0 ;
2016-08-26 09:58:03 +00:00
2019-08-09 23:07:39 +00:00
EX ld anticusp_factor = 1 ;
EX ld anticusp_dist ;
2018-02-27 18:37:57 +00:00
2019-08-09 23:07:39 +00:00
EX ld err_zero = 1e-3 , err_zero_current , current_total_error ;
2017-12-25 22:47:57 +00:00
2019-09-06 06:17:02 +00:00
EX int queueiter , qvalid , dt ;
2016-08-26 09:58:03 +00:00
2019-08-09 23:07:39 +00:00
EX rugpoint * finger_center ;
EX ld finger_range = .1 ;
EX ld finger_force = 1 ;
2018-01-29 15:28:06 +00:00
2019-08-09 23:07:39 +00:00
EX int rugdim ;
2019-05-20 11:42:32 +00:00
2019-09-06 06:17:02 +00:00
EX 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 ) {
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 ] ) ;
2019-02-27 18:34:05 +00:00
if ( zero_d ( 3 , vec [ i ] ) ) {
2017-12-27 09:52:54 +00:00
// '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 ] ) ;
}
2019-02-27 18:34:05 +00:00
vec [ i ] / = hypot_d ( 3 , vec [ i ] ) ;
2017-12-27 09:52:54 +00:00
}
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 ) {
2018-03-02 12:06:28 +00:00
if ( abs ( h [ 2 ] ) > 1e-4 ) {
2018-11-24 16:01:49 +00:00
println ( hlog , " Error: h[2] = " , h [ 2 ] ) ;
2018-03-02 12:06:28 +00:00
rug_failure = true ;
}
2017-12-25 22:47:57 +00:00
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 ; }
2019-02-27 18:34:05 +00:00
ld d2 = hypot_d ( 2 , 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 {
2019-02-27 18:34:05 +00:00
// if(zero_d(3, 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
2019-09-06 06:17:02 +00:00
EX void push_all_points ( int coord , ld val ) {
2017-12-27 09:52:54 +00:00
if ( ! val ) return ;
2018-06-22 12:47:24 +00:00
else for ( int i = 0 ; i < isize ( points ) ; i + + )
2017-12-27 09:52:54 +00:00
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 ;
2019-08-09 23:07:39 +00:00
EX 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
2019-11-27 00:01:20 +00:00
if ( euclid & & quotient & & ! bounded ) {
2019-12-08 09:59:09 +00:00
hyperpoint h1 = inverse ( models : : euclidean_spin ) * eumove ( euc : : eu . user_axes [ 1 ] ) * C0 ;
2019-02-27 18:34:05 +00:00
h1 / = sqhypot_d ( 2 , h1 ) ;
2018-12-02 20:05:13 +00:00
if ( nonorientable ) h1 / = 2 ;
m - > valid = good_shape = true ;
ld d = h1 [ 0 ] * h [ 1 ] - h1 [ 1 ] * h [ 0 ] ;
ld a = h [ 0 ] * h1 [ 0 ] + h [ 1 ] * h1 [ 1 ] ;
2018-12-06 11:55:37 +00:00
// m->flat = modelscale * hpxyz(d * 2 * M_PI, sin(a * 2 * M_PI), cos(a * 2 * M_PI));
USING_NATIVE_GEOMETRY ;
hyperpoint hpoint = ypush ( modelscale ) * xpush0 ( modelscale * d * 2 * M_PI ) ;
ld hpdist = hdist0 ( hpoint ) ;
2019-02-27 18:34:05 +00:00
ld z = hypot_d ( 2 , hpoint ) ;
2018-12-06 11:55:37 +00:00
if ( z = = 0 ) z = 1 ;
hpoint = hpoint * hpdist / z ;
m - > flat = hpxyz ( hpoint [ 0 ] , hpoint [ 1 ] * sin ( a * 2 * M_PI ) , hpoint [ 1 ] * cos ( a * 2 * M_PI ) ) ;
2018-12-02 20:05:13 +00:00
}
else 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 ) ;
2019-02-27 18:34:05 +00:00
ld d0 = hypot_d ( 2 , h ) ; if ( ! d0 ) d0 = 1 ;
2017-12-27 10:59:37 +00:00
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));
2018-08-19 14:28:36 +00:00
hpoint = xpush ( r ) * ypush ( d ) * xpush0 ( - r ) ;
2017-12-27 17:53:00 +00:00
hpoint [ 0 ] = - hpoint [ 0 ] ;
2017-12-27 10:59:37 +00:00
}
ld hpdist = hdist0 ( hpoint ) ;
2019-02-27 18:34:05 +00:00
ld z = hypot_d ( 2 , 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
}
2019-05-20 11:42:32 +00:00
else {
m - > flat = h ;
2019-08-17 21:28:41 +00:00
ld hd = h [ LDIM ] ;
2019-08-15 13:05:43 +00:00
for ( int d = GDIM ; d < rugdim ; d + + )
2019-05-20 11:42:32 +00:00
m - > flat [ d ] = ( hd - .99 ) * ( rand ( ) % 1000 - rand ( ) % 1000 ) / 1000 ;
}
2017-12-27 18:55:00 +00:00
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 ;
}
2019-08-09 23:07:39 +00:00
EX rugpoint * findRugpoint ( hyperpoint h ) {
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( points ) ; i + + )
2019-05-20 11:42:32 +00:00
if ( sqhypot_d ( rugdim , points [ i ] - > h - h ) < 1e-5 ) return points [ i ] ;
2017-12-27 05:31:47 +00:00
return NULL ;
}
2019-08-09 23:07:39 +00:00
EX rugpoint * findOrAddRugpoint ( hyperpoint h , double dist ) {
2017-12-27 05:31:47 +00:00
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 ) ;
}
2019-08-09 23:07:39 +00:00
EX bool edge_exists ( rugpoint * e1 , rugpoint * e2 ) {
2018-02-27 18:37:57 +00:00
for ( auto & e : e1 - > edges )
if ( e . target = = e2 )
return true ;
return false ;
}
2017-12-27 05:31:47 +00:00
void addEdge ( rugpoint * e1 , rugpoint * e2 , ld len = 1 ) {
2018-02-27 18:37:57 +00:00
if ( ! edge_exists ( e1 , e2 ) )
addNewEdge ( e1 , e2 , len ) ;
}
void add_anticusp_edge ( rugpoint * e1 , rugpoint * e2 , ld len = 1 ) {
for ( auto & e : e1 - > anticusp_edges )
if ( e . target = = e2 ) return ;
edge e ; e . len = len ;
e . target = e2 ; e1 - > anticusp_edges . push_back ( e ) ;
e . target = e1 ; e2 - > anticusp_edges . push_back ( e ) ;
2016-08-26 09:58:03 +00:00
}
2019-08-09 23:07:39 +00:00
EX void addTriangle ( rugpoint * t1 , rugpoint * t2 , rugpoint * t3 , ld len IS ( 1 ) ) {
2017-12-27 05:31:47 +00:00
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
}
2019-09-06 06:17:02 +00:00
EX void sort_rug_points ( ) {
2018-03-24 14:17:17 +00:00
sort ( points . begin ( ) , points . end ( ) , psort ) ;
}
2016-08-26 09:58:03 +00:00
void calcLengths ( ) {
2018-02-27 18:37:57 +00:00
for ( auto p : points )
for ( auto & edge : p - > edges )
edge . len = hdist ( p - > h , edge . target - > h ) * modelscale ;
2016-08-26 09:58:03 +00:00
}
2020-01-02 15:56:58 +00:00
EX void calcparam_rug ( ) {
2018-11-20 18:01:10 +00:00
auto cd = current_display ;
cd - > xtop = cd - > ytop = 0 ;
cd - > xsize = cd - > ysize = TEXTURESIZE ;
cd - > xcenter = cd - > ycenter = cd - > scrsize = HTEXTURESIZE ;
cd - > radius = cd - > scrsize * vid . scale ;
2017-11-06 18:24:02 +00:00
}
2019-08-09 23:07:39 +00:00
EX void buildTorusRug ( ) {
2017-11-06 18:24:02 +00:00
2018-11-20 18:01:10 +00:00
calcparam_rug ( ) ;
2019-08-09 22:58:50 +00:00
models : : configure ( ) ;
2017-11-06 18:24:02 +00:00
2019-12-08 09:59:09 +00:00
auto p1 = to_loc ( euc : : eu . user_axes [ 0 ] ) ;
auto p2 = to_loc ( euc : : eu . user_axes [ 1 ] ) ;
2019-11-28 20:55:20 +00:00
2019-12-08 09:59:09 +00:00
hyperpoint xh = euc : : eumove ( p1 ) * C0 - C0 ;
hyperpoint yh = euc : : eumove ( p2 ) * C0 - C0 ;
2019-11-28 20:55:20 +00:00
if ( nonorientable ) yh * = 2 ;
2017-12-28 17:39:49 +00:00
2019-12-08 23:40:51 +00:00
bool flipped = false ; // sqhypot_d(2, xh) < sqhypot_d(2, yh);
2017-11-06 18:24:02 +00:00
2019-11-28 20:55:20 +00:00
if ( flipped ) swap ( xh , yh ) ;
2018-05-28 17:10:57 +00:00
2019-11-28 20:55:20 +00:00
cell * gs = currentmap - > gamestart ( ) ;
2017-11-06 18:24:02 +00:00
2019-11-28 20:55:20 +00:00
ld xfactor , yfactor ;
2017-11-06 18:24:02 +00:00
2019-11-28 20:55:20 +00:00
ld factor2 = sqhypot_d ( 2 , xh ) / sqhypot_d ( 2 , yh ) ;
ld factor = sqrt ( factor2 ) ;
yfactor = sqrt ( 1 / ( 1 + factor2 ) ) ;
xfactor = factor * yfactor ;
2018-06-12 17:26:34 +00:00
2019-11-28 20:55:20 +00:00
transmatrix T = build_matrix ( xh , yh , C0 , C03 ) ;
transmatrix iT = inverse ( T ) ;
if ( gwhere = = gSphere )
modelscale = hypot_d ( 2 , xh ) * xfactor * 2 * M_PI ;
2018-06-12 17:26:34 +00:00
2017-12-28 17:39:49 +00:00
map < pair < int , int > , rugpoint * > glues ;
2019-12-08 23:40:51 +00:00
ld mx = 0 ;
for ( int i = 0 ; i < 1000 ; i + + )
mx = max ( mx , hypot ( xfactor , yfactor * sin ( i ) ) / ( 1 - yfactor * cos ( i ) ) ) ;
println ( hlog , " mx = " , mx ) ;
2019-11-28 20:55:20 +00:00
auto addToruspoint = [ & ] ( hyperpoint h ) {
2017-11-06 18:24:02 +00:00
auto r = addRugpoint ( C0 , 0 ) ;
hyperpoint onscreen ;
2019-11-28 20:55:20 +00:00
hyperpoint h1 = gmatrix [ gs ] * T * h ;
applymodel ( h1 , onscreen ) ;
2017-11-06 18:24:02 +00:00
r - > x1 = onscreen [ 0 ] ;
r - > y1 = onscreen [ 1 ] ;
2019-11-28 20:55:20 +00:00
double alpha = - h [ 0 ] * 2 * M_PI ;
double beta = h [ 1 ] * 2 * M_PI ;
2017-12-28 17:39:49 +00:00
2019-12-08 23:40:51 +00:00
ld ax = alpha + 1.124651 , bx = beta + 1.214893 ;
ld x = xfactor * sin ( ax ) , y = xfactor * cos ( ax ) , z = yfactor * sin ( bx ) , t = yfactor * cos ( bx ) ;
ld d ;
ld hxyz = sqrt ( x * x + y * y + z * z ) ;
if ( gwhere = = gNormal ) {
d = hxyz / ( 1 - t ) / mx ;
d * = .95 ;
hyperpoint test = hpxy ( d , 0 ) ;
test = perspective_to_space ( test , 1 , gcHyperbolic ) ;
d = acosh ( test [ 2 ] ) / hxyz ;
2018-05-28 17:10:57 +00:00
}
2019-12-08 23:40:51 +00:00
else {
d = ( gwhere = = gSphere ) ? acos ( t ) / hxyz : modelscale * 3 / ( 1 - t ) / mx ;
}
r - > flat = r - > h = hpxyz ( x * d , y * d , z * d ) ;
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 ) ; } ;
2019-11-28 20:55:20 +00:00
auto p = make_pair ( gluefun ( h [ 0 ] ) , gluefun ( h [ 1 ] ) ) ;
2017-12-28 17:39:49 +00:00
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
2019-11-28 20:55:20 +00:00
int rugmax = ( int ) sqrt ( vertex_limit / isize ( currentmap - > allcells ( ) ) ) ;
2017-12-27 09:52:54 +00:00
if ( rugmax < 1 ) rugmax = 1 ;
if ( rugmax > 16 ) rugmax = 16 ;
2017-11-07 13:39:26 +00:00
ld rmd = rugmax ;
2019-11-28 20:55:20 +00:00
hyperpoint sx = iT * ( currentmap - > adj ( gs , 0 ) * C0 - C0 ) / rmd ;
hyperpoint sy = iT * ( currentmap - > adj ( gs , 1 ) * C0 - C0 ) / rmd ;
for ( int leaf = 0 ; leaf < ( nonorientable ? 2 : 1 ) ; leaf + + )
for ( cell * c : currentmap - > allcells ( ) ) {
hyperpoint h = iT * calc_relative_matrix ( c , gs , C0 ) * C0 ;
2017-12-28 17:39:49 +00:00
2019-11-28 20:55:20 +00:00
if ( leaf ) h [ flipped ? 0 : 1 ] + = .5 ;
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 + + )
2019-11-28 20:55:20 +00:00
rugarr [ yy ] [ xx ] = addToruspoint ( h + xx * sx + yy * sy ) ;
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 ) ,
2018-05-28 17:10:21 +00:00
addTriangle ( rugarr [ yy ] [ xx + 1 ] , rugarr [ yy ] [ xx ] , 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 ) ) ) ;
2019-11-28 20:55:20 +00:00
if ( 1 ) vid . scale = 1 / maxz ;
if ( 1 ) for ( auto p : points )
p - > x1 = ( 1 + vid . scale * p - > x1 ) / 2 ,
p - > y1 = ( 1 - vid . scale * p - > y1 ) / 2 ;
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 + + ;
2018-11-24 16:01:49 +00:00
println ( hlog , " qvalid = " , 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 ;
}
2019-08-09 23:07:39 +00:00
EX void verify ( ) {
2017-12-27 10:59:37 +00:00
vector < ld > ratios ;
for ( auto m : points )
for ( auto & e : m - > edges ) {
auto m2 = e . target ;
ld l = e . len ;
2019-05-20 11:42:32 +00:00
ld l0 ;
2019-08-09 22:05:42 +00:00
if ( fast_euclidean ) l0 = sqhypot_d ( rugdim , m - > flat - m2 - > flat ) ;
2019-05-20 11:42:32 +00:00
else {
normalizer n ( m - > flat , m2 - > flat ) ;
hyperpoint h1 = n ( m - > flat ) ;
hyperpoint h2 = n ( m2 - > flat ) ;
l0 = hdist ( h1 , h2 ) ;
}
2017-12-27 10:59:37 +00:00
ratios . push_back ( l0 / l ) ;
}
2018-11-24 16:01:49 +00:00
println ( hlog , " Length verification: " ) ;
2017-12-27 10:59:37 +00:00
sort ( ratios . begin ( ) , ratios . end ( ) ) ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( ratios ) ; i + = isize ( ratios ) / 10 )
2018-11-24 16:01:49 +00:00
println ( hlog , ratios [ i ] ) ;
println ( hlog ) ;
2017-12-27 10:59:37 +00:00
}
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 ;
}
2018-02-27 18:37:57 +00:00
2019-08-09 23:07:39 +00:00
EX void buildRug ( ) {
2016-08-26 09:58:03 +00:00
2018-03-24 14:17:17 +00:00
need_mouseh = true ;
good_shape = false ;
2019-11-27 00:01:20 +00:00
if ( euclid & & bounded ) {
2017-12-27 09:52:54 +00:00
good_shape = true ;
2017-11-06 18:24:02 +00:00
buildTorusRug ( ) ;
return ;
}
2019-11-13 23:26:50 +00:00
celllister cl ( centerover ? centerover : cwt . at , get_sightrange ( ) , vertex_limit , NULL ) ;
2017-12-29 11:54:50 +00:00
2016-08-26 09:58:03 +00:00
map < cell * , rugpoint * > vptr ;
2017-12-29 11:54:50 +00:00
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( cl . lst ) ; i + + )
2018-08-17 14:47:06 +00:00
vptr [ cl . lst [ i ] ] = addRugpoint ( 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 ;
2018-08-24 19:47:09 +00:00
2019-12-14 10:42:16 +00:00
if ( arcm : : in ( ) | | ( euclid & & quotient ) ) {
2018-08-24 19:47:09 +00:00
rugpoint * p [ MAX_EDGE + 1 ] ;
for ( int j = 0 ; j < c - > type ; j + + ) p [ j ] = findOrAddRugpoint ( ggmatrix ( c ) * get_corner_position ( c , j ) , v - > dist ) ;
for ( int j = 0 ; j < c - > type ; j + + ) addTriangle ( v , p [ j ] , p [ ( j + 1 ) % c - > type ] ) ;
2018-12-02 20:05:13 +00:00
2019-11-27 00:01:20 +00:00
if ( ( euclid & & quotient ) & & nonorientable ) {
2019-12-08 09:59:09 +00:00
transmatrix T = ggmatrix ( c ) * eumove ( euc : : eu . user_axes [ 1 ] ) ;
2018-12-02 20:05:13 +00:00
rugpoint * Tv = addRugpoint ( T * C0 , 0 ) ;
for ( int j = 0 ; j < c - > type ; j + + ) p [ j ] = findOrAddRugpoint ( T * get_corner_position ( c , j ) , v - > dist ) ;
for ( int j = 0 ; j < c - > type ; j + + ) addTriangle ( Tv , p [ j ] , p [ ( j + 1 ) % c - > type ] ) ;
}
2018-08-24 19:47:09 +00:00
}
else for ( int j = 0 ; j < c - > type ; j + + ) try {
2018-08-17 22:46:45 +00:00
cell * c2 = c - > move ( 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);
2018-08-17 22:46:45 +00:00
cell * c3 = c - > modmove ( j + 1 ) ;
2017-12-29 11:54:50 +00:00
rugpoint * w2 = vptr . at ( c3 ) ;
if ( a4 ) {
2018-08-17 22:46:45 +00:00
cell * c4 = ( cellwalker ( c , j ) + wstep - 1 ) . cpeek ( ) ;
2017-12-29 11:54:50 +00:00
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
}
2018-06-07 11:58:00 +00:00
catch ( out_of_range & ) { }
2016-08-26 09:58:03 +00:00
}
2018-11-24 16:01:49 +00:00
println ( hlog , " vertices = " , isize ( points ) , " triangles= " , isize ( triangles ) ) ;
2016-08-26 09:58:03 +00:00
2018-02-27 18:27:20 +00:00
if ( subdivide_first )
for ( int i = 0 ; i < 20 & & subdivide_further ( ) ; i + + )
subdivide ( ) ;
2018-03-24 14:17:17 +00:00
sort_rug_points ( ) ;
2017-12-27 10:59:37 +00:00
2018-02-27 18:37:57 +00:00
calcLengths ( ) ;
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 ;
2019-09-06 06:17:02 +00:00
EX void enqueue ( rugpoint * m ) {
2016-08-26 09:58:03 +00:00
if ( m - > inqueue ) return ;
pqueue . push ( m ) ;
m - > inqueue = true ;
}
2018-02-27 18:37:57 +00:00
bool force_euclidean ( rugpoint & m1 , rugpoint & m2 , double rd , bool is_anticusp = false , double d1 = 1 , double d2 = 1 ) {
2017-12-27 09:52:54 +00:00
if ( ! m1 . valid | | ! m2 . valid ) return false ;
2016-08-26 09:58:03 +00:00
// double rd = hdist(m1.h, m2.h) * xd;
2019-08-09 22:05:42 +00:00
double t = sqhypot_d ( rugdim , m1 . flat - m2 . flat ) ;
2018-02-27 18:37:57 +00:00
if ( is_anticusp & & t > rd * rd ) return false ;
2016-08-26 09:58:03 +00:00
t = sqrt ( t ) ;
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 ;
2016-08-26 09:58:03 +00:00
double force = ( t - rd ) / t / 2 ; // 20.0;
2019-05-20 11:42:32 +00:00
for ( int i = 0 ; i < rugdim ; i + + ) {
2016-08-26 09:58:03 +00:00
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
}
2018-02-27 18:37:57 +00:00
bool force ( rugpoint & m1 , rugpoint & m2 , double rd , bool is_anticusp = false , double d1 = 1 , double d2 = 1 ) {
2017-12-27 09:52:54 +00:00
if ( ! m1 . valid | | ! m2 . valid ) return false ;
2017-12-25 22:47:57 +00:00
if ( gwhere = = gEuclid & & fast_euclidean ) {
2018-02-27 18:37:57 +00:00
return force_euclidean ( m1 , m2 , rd , is_anticusp , d1 , d2 ) ;
2017-12-25 22:47:57 +00:00
}
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 ) ;
2018-02-27 18:37:57 +00:00
if ( is_anticusp & & t > rd ) return false ;
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
2018-08-19 14:28:36 +00:00
f1 = iT1 * xpush0 ( d1 * forcev ) ;
f2 = iT1 * xpush0 ( t - d2 * forcev ) ;
2017-12-25 22:47:57 +00:00
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 ;
2019-08-09 23:07:39 +00:00
EX void preset ( rugpoint * m ) {
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) return ;
2016-08-26 09:58:03 +00:00
int q = 0 ;
hyperpoint h ;
for ( int i = 0 ; i < 3 ; i + + ) h [ i ] = 0 ;
2017-12-27 20:08:31 +00:00
preset_points . clear ( ) ;
2018-06-22 12:47:24 +00:00
for ( int j = 0 ; j < isize ( m - > edges ) ; j + + )
2016-08-26 09:58:03 +00:00
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 ;
2018-06-22 12:47:24 +00:00
for ( int j2 = 0 ; j2 < isize ( a - > edges ) ; j2 + + )
2016-08-26 09:58:03 +00:00
if ( a - > edges [ j2 ] . target = = b ) blen = a - > edges [ j2 ] . len ;
if ( blen < = 0 ) continue ;
2018-06-22 12:47:24 +00:00
for ( int j2 = 0 ; j2 < isize ( a - > edges ) ; j2 + + )
for ( int k2 = 0 ; k2 < isize ( b - > edges ) ; k2 + + )
2016-08-26 09:58:03 +00:00
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 + + ;
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
}
2019-05-20 11:42:32 +00:00
ld sse ( const hyperpoint & h ) {
2017-12-27 20:08:31 +00:00
ld sse = 0 ;
for ( auto & p : preset_points ) {
ld l = p . first ;
2019-05-20 11:42:32 +00:00
ld l0 ;
if ( fast_euclidean )
2019-08-09 22:05:42 +00:00
l0 = hypot_d ( rugdim , h - p . second - > flat ) ;
2019-05-20 11:42:32 +00:00
else {
normalizer n ( h , p . second - > flat ) ;
hyperpoint h1 = n ( h ) ;
hyperpoint h2 = n ( p . second - > flat ) ;
l0 = hdist ( h1 , h2 ) ;
}
2017-12-27 20:08:31 +00:00
sse + = ( l0 - l ) * ( l0 - l ) ;
}
return sse ;
}
2019-08-09 23:07:39 +00:00
EX void optimize ( rugpoint * m , bool do_preset ) {
2017-12-27 20:08:31 +00:00
if ( do_preset ) {
preset ( m ) ;
2018-06-22 12:47:24 +00:00
// int ed0 = isize(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 ) ;
2019-08-15 13:05:43 +00:00
if ( gwhere > = gSphere | | GDIM = = 3 ) {
2017-12-27 20:08:31 +00:00
ld cur = sse ( m - > flat ) ;
for ( int it = 0 ; it < 500 ; it + + ) {
ld ex = exp ( - it / 60 ) ;
again :
hyperpoint last = m - > flat ;
2019-05-20 11:42:32 +00:00
m - > flat [ ( it / 2 ) % rugdim ] + = ( ( it & 1 ) ? 1 : - 1 ) * ex ;
2017-12-27 20:08:31 +00:00
ld now = sse ( m - > flat ) ;
if ( now < cur ) { cur = now ; ex * = 1.2 ; goto again ; }
else m - > flat = last ;
}
}
}
for ( int it = 0 ; it < 50 ; it + + )
2018-06-22 12:47:24 +00:00
for ( int j = 0 ; j < isize ( m - > edges ) ; j + + )
2018-02-27 18:37:57 +00:00
force ( * m , * m - > edges [ j ] . target , m - > edges [ j ] . len , false , 1 , 0 ) ;
2017-12-27 20:08:31 +00:00
}
2016-08-26 09:58:03 +00:00
int divides = 0 ;
bool stop = false ;
2019-08-09 23:07:39 +00:00
EX bool subdivide_further ( ) {
2019-11-27 00:01:20 +00:00
if ( euclid & & bounded ) return false ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) return false ;
2018-06-22 12:47:24 +00:00
return isize ( points ) * 4 < vertex_limit ;
2017-12-27 09:52:54 +00:00
}
2019-08-09 23:07:39 +00:00
EX void subdivide ( ) {
2018-06-22 12:47:24 +00:00
int N = isize ( 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 ) {
2018-11-24 16:01:49 +00:00
println ( hlog , " Euclidean -- full precision " ) ;
2017-12-27 10:59:37 +00:00
stop = true ;
}
else {
err_zero_current / = 2 ;
2018-11-24 16:01:49 +00:00
println ( hlog , " increasing precision to " , err_zero_current ) ;
2017-12-27 10:59:37 +00:00
for ( auto p : points ) enqueue ( p ) ;
}
2017-12-27 05:31:47 +00:00
return ;
}
2018-11-24 16:01:49 +00:00
println ( hlog , " subdivide " , make_pair ( N , isize ( triangles ) ) ) ;
2018-03-24 14:17:17 +00:00
need_mouseh = true ;
2016-08-26 09:58:03 +00:00
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 ] ;
2018-06-22 12:47:24 +00:00
for ( int j = 0 ; j < isize ( m - > edges ) ; j + + ) {
2016-08-26 09:58:03 +00:00
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
normalizer n ( m - > flat , m2 - > flat ) ;
hyperpoint h1 = n ( m - > flat ) ;
hyperpoint h2 = n ( m2 - > flat ) ;
mm - > flat = n [ mid ( h1 , h2 ) ] ;
}
2018-02-27 18:37:57 +00:00
mm - > valid = m - > valid & & m2 - > valid ;
if ( mm - > valid ) qvalid + + ;
2016-08-26 09:58:03 +00:00
mm - > inqueue = false ; enqueue ( mm ) ;
}
m - > edges . clear ( ) ;
}
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( otriangles ) ; i + + )
2016-08-26 09:58:03 +00:00
addTriangle1 ( otriangles [ i ] . m [ 0 ] , otriangles [ i ] . m [ 1 ] , otriangles [ i ] . m [ 2 ] ) ;
calcLengths ( ) ;
2018-11-24 16:01:49 +00:00
println ( hlog , " result " , make_tuple ( isize ( points ) , isize ( triangles ) ) ) ;
2016-08-26 09:58:03 +00:00
}
2019-08-09 23:07:39 +00:00
EX ld slow_modeldist ( const hyperpoint & h1 , const hyperpoint & h2 ) {
2018-02-27 18:37:57 +00:00
normalizer n ( h1 , h2 ) ;
hyperpoint f1 = n ( h1 ) ;
hyperpoint f2 = n ( h2 ) ;
return hdist ( f1 , f2 ) ;
}
typedef array < ld , 4 > hyperpoint4 ;
hyperpoint4 azeq_to_4 ( const hyperpoint & h ) {
array < ld , 4 > res ;
2019-02-27 18:34:05 +00:00
ld rad = hypot_d ( 3 , h ) ;
2018-02-27 18:37:57 +00:00
res [ 3 ] = cos ( rad ) ;
ld sr = sin ( rad ) / rad ;
for ( int j = 0 ; j < 3 ; j + + ) res [ j ] = h [ j ] * sr ;
return res ;
}
2019-08-09 23:07:39 +00:00
EX ld modeldist ( const hyperpoint & h1 , const hyperpoint & h2 ) {
2018-02-27 18:37:57 +00:00
if ( gwhere = = gSphere ) {
hyperpoint4 coord [ 2 ] = { azeq_to_4 ( h1 ) , azeq_to_4 ( h2 ) } ;
ld edist = 0 ;
for ( int j = 0 ; j < 4 ; j + + ) edist + = sqr ( coord [ 0 ] [ j ] - coord [ 1 ] [ j ] ) ;
return 2 * asin ( sqrt ( edist ) / 2 ) ;
}
return slow_modeldist ( h1 , h2 ) ;
}
typedef long long bincode ;
const bincode sY = ( 1 < < 16 ) ;
const bincode sZ = sY * sY ;
const bincode sT = sY * sY * sY ;
bincode acd_bin ( ld x ) {
return ( int ) floor ( x / anticusp_dist + .5 ) ;
}
bincode get_bincode ( hyperpoint h ) {
switch ( ginf [ gwhere ] . cclass ) {
2019-10-03 18:36:15 +00:00
case gcEuclid : case gcSolNIH : case gcNil : case gcProduct : case gcSL2 :
2018-02-27 18:37:57 +00:00
return acd_bin ( h [ 0 ] ) + acd_bin ( h [ 1 ] ) * sY + acd_bin ( h [ 2 ] ) * sZ ;
case gcHyperbolic :
2019-02-27 18:34:05 +00:00
return acd_bin ( hypot_d ( 3 , h ) ) ;
2018-02-27 18:37:57 +00:00
case gcSphere : {
auto p = azeq_to_4 ( h ) ;
return acd_bin ( p [ 0 ] ) + acd_bin ( p [ 1 ] ) * sY + acd_bin ( p [ 2 ] ) * sZ + acd_bin ( p [ 3 ] ) * sT ;
}
}
return 0 ;
}
void generate_deltas ( vector < bincode > & target , int dim , bincode offset ) {
if ( dim = = 0 ) {
if ( offset > 0 ) target . push_back ( offset ) ;
}
else {
generate_deltas ( target , dim - 1 , offset * sY ) ;
generate_deltas ( target , dim - 1 , offset * sY + 1 ) ;
generate_deltas ( target , dim - 1 , offset * sY - 1 ) ;
}
}
int detect_cusp_at ( rugpoint * p , rugpoint * q ) {
if ( hdist ( p - > h , q - > h ) * modelscale < = anticusp_dist )
return 0 ;
2018-02-27 18:47:48 +00:00
else if ( modeldist ( p - > flat , q - > flat ) > anticusp_dist - err_zero_current )
2018-02-27 18:37:57 +00:00
return 1 ;
else {
add_anticusp_edge ( p , q ) ;
enqueue ( p ) ;
enqueue ( q ) ;
return 2 ;
}
}
int detect_cusps ( ) {
ld max_edge_length = 0 ;
for ( auto p : points )
for ( auto e : p - > edges )
max_edge_length = max ( max_edge_length , e . len ) ;
anticusp_dist = anticusp_factor * max_edge_length ;
2018-12-12 01:49:23 +00:00
array < int , 3 > stats = { { 0 , 0 , 0 } } ;
2018-02-27 18:37:57 +00:00
map < bincode , vector < rugpoint * > > code_to_point ;
for ( auto p : points ) if ( p - > valid )
code_to_point [ get_bincode ( p - > flat ) ] . push_back ( p ) ;
vector < bincode > deltas ;
generate_deltas ( deltas , gwhere = = gEuclid ? 3 : gwhere = = gNormal ? 1 : 4 , 0 ) ;
for ( auto b : code_to_point ) {
bincode at = b . first ;
for ( auto p : b . second )
for ( auto q : b . second )
if ( p < q ) stats [ detect_cusp_at ( p , q ) ] + + ;
for ( bincode bc : deltas )
if ( code_to_point . count ( at + bc ) )
for ( auto p : b . second )
for ( auto q : code_to_point [ at + bc ] )
stats [ detect_cusp_at ( p , q ) ] + + ;
}
/* printf("testing\n");
int stats2 [ 3 ] = { 0 , 0 , 0 } ;
for ( auto p : points ) if ( p - > valid )
for ( auto q : points ) if ( q - > valid ) if ( p < q ) {
stats2 [ detect_cusp_at ( p , q ) ] + + ;
}
printf ( " cusp stats: %d/%d/%d | %d/%d/%d \n " , stats [ 0 ] , stats [ 1 ] , stats [ 2 ] , stats2 [ 0 ] , stats2 [ 1 ] , stats2 [ 2 ] ) ; */
2018-11-24 16:01:49 +00:00
println ( hlog , " cusp stats: " , stats ) ;
2018-02-27 18:37:57 +00:00
return stats [ 2 ] ;
}
2019-08-09 23:07:39 +00:00
EX void addNewPoints ( ) {
2016-08-26 09:58:03 +00:00
2018-02-27 18:37:57 +00:00
if ( anticusp_factor & & detect_cusps ( ) )
return ;
2019-11-27 00:01:20 +00:00
if ( ( euclid & & quotient ) | | qvalid = = isize ( points ) ) {
2016-08-26 09:58:03 +00:00
subdivide ( ) ;
return ;
}
2019-05-04 16:28:03 +00:00
ld dist = hdist0 ( points [ qvalid ] - > h ) + .1e-6 ;
2016-08-26 09:58:03 +00:00
int oqvalid = qvalid ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( points ) ; i + + ) {
2016-08-26 09:58:03 +00:00
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 + + ;
2018-03-24 14:17:17 +00:00
need_mouseh = true ;
2016-08-26 09:58:03 +00:00
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 ) ;
}
}
2018-11-24 16:01:49 +00:00
if ( qvalid ! = oqvalid ) { println ( hlog , " adding new points " , make_tuple ( oqvalid , qvalid , isize ( points ) , dist , dt , queueiter ) ) ; }
2016-08-26 09:58:03 +00:00
}
2019-08-09 23:07:39 +00:00
EX void physics ( ) {
2016-08-26 09:58:03 +00:00
2019-02-27 18:34:18 +00:00
# if CAP_CRYSTAL
2018-11-30 19:29:14 +00:00
if ( in_crystal ( ) ) {
crystal : : build_rugdata ( ) ;
return ;
}
2019-02-27 18:34:18 +00:00
# endif
2018-11-30 19:29:14 +00:00
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 ;
2018-02-27 18:37:57 +00:00
for ( auto & e : m - > edges )
moved = force ( * m , * e . target , e . len ) | | moved ;
for ( auto & e : m - > anticusp_edges )
moved = force ( * m , * e . target , anticusp_dist , true ) | | moved ;
2017-12-27 09:52:54 +00:00
2018-03-24 14:17:17 +00:00
if ( moved ) enqueue ( m ) , need_mouseh = true ;
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 ) {
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 ) {
2019-02-27 18:34:05 +00:00
ld rad = hypot_d ( 3 , h ) ;
2017-12-27 13:12:27 +00:00
// 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 */
bool project_ods ( hyperpoint azeq , hyperpoint & h1 , hyperpoint & h2 , bool eye ) {
2018-05-15 21:29:44 +00:00
USING_NATIVE_GEOMETRY ;
2018-01-28 11:20:21 +00:00
2019-02-27 18:34:05 +00:00
ld d = hypot_d ( 3 , azeq ) ;
2018-05-15 21:29:44 +00:00
ld sindbd = sin_auto ( d ) / d , cosd = cos_auto ( d ) ;
2018-01-28 11:20:21 +00:00
2019-10-21 20:42:27 +00:00
return ods : : project ( hyperpoint ( azeq [ 0 ] * sindbd , azeq [ 1 ] * sindbd , azeq [ 2 ] * sindbd , cosd ) , h1 , h2 , eye ) ;
2018-01-28 11:20:21 +00:00
// 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);
// 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
2019-03-30 16:46:50 +00:00
vector < glhr : : ct_vertex > cp_array ;
2016-08-26 09:58:03 +00:00
void drawTriangle ( triangle & t ) {
2019-03-30 16:46:50 +00:00
int num = t . m [ 2 ] ? 3 : 2 ;
for ( int i = 0 ; i < num ; i + + ) {
2017-12-27 05:31:47 +00:00
if ( ! t . m [ i ] - > valid ) return ;
2018-11-30 13:53:08 +00:00
// if(t.m[i]->dist >= get_sightrange()+.51) return;
2017-12-27 05:31:47 +00:00
}
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
2019-10-21 20:42:27 +00:00
if ( vid . stereo_mode = = sODS ) {
2018-01-28 11:20:21 +00:00
hyperpoint pts [ 3 ] ;
2019-03-30 16:46:50 +00:00
// not implemented
if ( num = = 2 ) return ;
for ( int i = 0 ; i < num ; i + + )
pts [ i ] = t . m [ i ] - > getglue ( ) - > flat ;
2018-01-28 11:20:21 +00:00
hyperpoint hc = ( pts [ 1 ] - pts [ 0 ] ) ^ ( pts [ 2 ] - pts [ 0 ] ) ;
2019-02-27 18:34:05 +00:00
double hch = hypot_d ( 3 , hc ) ;
2018-02-09 02:40:46 +00:00
2018-02-27 18:38:45 +00:00
ld col = ( 2 + hc [ 0 ] / hch ) / 3 ;
2018-05-15 21:29:44 +00:00
bool natsph = among ( gwhere , gSphere , gElliptic ) ;
2018-01-28 11:20:21 +00:00
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 ] ;
}
2018-05-15 21:29:44 +00:00
if ( natsph ) {
if ( raddif ( h [ 4 ] [ 0 ] , h [ 0 ] [ 0 ] ) < raddif ( h [ 1 ] [ 0 ] , h [ 0 ] [ 0 ] ) )
swap ( h [ 1 ] , h [ 4 ] ) ;
if ( raddif ( h [ 5 ] [ 0 ] , h [ 0 ] [ 0 ] ) < raddif ( h [ 2 ] [ 0 ] , h [ 0 ] [ 0 ] ) )
swap ( h [ 5 ] , h [ 2 ] ) ;
}
else {
if ( h [ 0 ] [ 2 ] < 0 ) swap ( h [ 0 ] , h [ 3 ] ) ;
if ( h [ 1 ] [ 2 ] < 0 ) swap ( h [ 1 ] , h [ 4 ] ) ;
if ( h [ 2 ] [ 2 ] < 0 ) swap ( h [ 2 ] , h [ 5 ] ) ;
}
2018-01-28 11:20:21 +00:00
if ( abs ( h [ 1 ] [ 1 ] - h [ 0 ] [ 1 ] ) > M_PI / 2 ) return ;
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 + + ) {
2018-02-27 18:38:45 +00:00
ct_array . emplace_back (
hpxyz ( h [ s + i ] [ 0 ] + 2 * M_PI * x , h [ s + i ] [ 1 ] , h [ s + i ] [ 2 ] ) ,
t . m [ i ] - > x1 , t . m [ i ] - > y1 ,
col ) ;
2018-01-28 11:20:21 +00:00
}
2018-05-15 21:29:44 +00:00
if ( ! natsph ) break ;
2018-01-28 11:20:21 +00:00
}
}
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 ;
2019-03-30 16:46:50 +00:00
for ( int i = 0 ; i < num ; i + + ) 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
2019-03-30 16:46:50 +00:00
ld col = 1 ;
if ( num = = 3 ) {
hyperpoint hc = ( h [ 1 ] - h [ 0 ] ) ^ ( h [ 2 ] - h [ 0 ] ) ;
double hch = hypot_d ( 3 , hc ) ;
col = ( 2 + hc [ 0 ] / hch ) / 3 ;
}
2018-02-09 02:40:46 +00:00
2019-03-30 16:46:50 +00:00
for ( int i = 0 ; i < num ; i + + )
( num = = 3 ? ct_array : cp_array ) . emplace_back ( h [ i ] , t . m [ i ] - > x1 , t . m [ i ] - > y1 , col ) ;
2016-08-26 09:58:03 +00:00
}
2019-08-09 23:07:39 +00:00
EX struct renderbuffer * glbuf ;
2016-08-26 09:58:03 +00:00
2019-08-09 23:07:39 +00:00
EX void prepareTexture ( ) {
2019-11-03 14:52:19 +00:00
ensure_glbuf ( ) ;
if ( ! glbuf ) { rug : : close ( ) ; return ; }
2018-02-11 01:19:49 +00:00
resetbuffer rb ;
2018-11-17 18:24:02 +00:00
dynamicval < eStereo > d ( vid . stereo_mode , sOFF ) ;
2019-11-08 13:57:22 +00:00
dynamicval < ld > dl ( levellines , 0 ) ;
2018-11-20 18:01:10 +00:00
calcparam_rug ( ) ;
2019-08-09 22:58:50 +00:00
models : : configure ( ) ;
2016-08-26 09:58:03 +00:00
2018-02-01 12:42:47 +00:00
glbuf - > enable ( ) ;
glbuf - > clear ( 0 ) ;
2016-08-26 09:58:03 +00:00
2018-02-01 12:42:47 +00:00
ptds . clear ( ) ;
2019-03-30 16:47:16 +00:00
# if CAP_QUEUE
draw_boundary ( 0 ) ;
draw_boundary ( 1 ) ;
draw_model_elements ( ) ;
# endif
2018-02-01 12:42:47 +00:00
drawthemap ( ) ;
if ( mousing & & ! renderonce ) {
for ( int i = 0 ; i < numplayers ( ) ; i + + ) if ( multi : : playerActive ( i ) )
2018-08-17 14:47:06 +00:00
queueline ( tC0 ( ggmatrix ( playerpos ( i ) ) ) , mouseh , 0xFF00FF , 8 + vid . linequality ) ;
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 ) ;
2019-12-26 22:38:28 +00:00
queuestr ( V , 0.5 , " X " , 0xFFFFFFFF , 2 ) ;
2018-02-01 12:42:47 +00:00
for ( int i = 0 ; i < 72 ; i + + )
2018-08-19 14:28:36 +00:00
queueline ( V * xspinpush0 ( i * M_PI / 32 , finger_range ) , V * xspinpush0 ( ( i + 1 ) * M_PI / 32 , finger_range ) , 0xFFFFFFFF , vid . linequality ) ;
2016-08-26 09:58:03 +00:00
}
2018-02-01 12:42:47 +00:00
drawqueue ( ) ;
2018-11-20 18:01:10 +00:00
calcparam ( ) ;
2018-02-11 01:19:49 +00:00
rb . reset ( ) ;
2016-08-26 09:58:03 +00:00
}
double xview , yview ;
2019-08-09 23:07:39 +00:00
EX bool no_fog ;
2018-05-15 21:30:37 +00:00
2019-09-06 06:17:02 +00:00
EX ld lowrug = 1e-2 ;
EX ld hirug = 1e3 ;
2018-05-15 21:30:37 +00:00
2019-09-06 06:17:02 +00:00
EX GLuint alternate_texture ;
2018-05-18 15:34:12 +00:00
2019-09-06 06:17:02 +00:00
EX bool invert_depth ;
2018-09-03 14:32:11 +00:00
2019-08-09 23:07:39 +00:00
EX void drawRugScene ( ) {
2018-02-01 12:42:47 +00:00
glbuf - > use_as_texture ( ) ;
2018-05-18 15:34:12 +00:00
if ( alternate_texture )
glBindTexture ( GL_TEXTURE_2D , alternate_texture ) ;
2016-08-26 09:58:03 +00:00
2017-07-04 13:38:33 +00:00
if ( backcolor = = 0 )
2018-06-28 00:49:26 +00:00
glClearColor ( 0.05f , 0.05f , 0.05f , 1.0f ) ;
2017-07-04 13:38:33 +00:00
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-09-03 14:32:11 +00:00
glClearDepthf ( invert_depth ? - 1.0f : 1.0f ) ;
2018-02-04 00:04:29 +00:00
# else
2018-09-03 14:32:11 +00:00
glClearDepth ( invert_depth ? - 1.0f : 1.0f ) ;
2018-02-04 00:04:29 +00:00
# endif
2016-08-26 09:58:03 +00:00
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
glDisable ( GL_BLEND ) ;
2019-10-21 20:34:20 +00:00
current_display - > next_shader_flags = GF_LIGHTFOG | GF_TEXTURE ;
2018-03-24 14:16:03 +00:00
glhr : : set_depthtest ( true ) ;
2019-06-06 17:53:37 +00:00
glhr : : set_depthwrite ( true ) ;
2018-09-03 14:32:11 +00:00
glDepthFunc ( invert_depth ? GL_GREATER : GL_LESS ) ;
2016-08-26 09:58:03 +00:00
2018-11-17 18:24:02 +00:00
for ( int ed = current_display - > stereo_active ( ) & & vid . stereo_mode ! = sODS ? - 1 : 0 ; ed < 2 ; ed + = 2 ) {
2018-02-03 12:41:49 +00:00
use_precompute = false ;
2018-02-11 18:08:17 +00:00
ct_array . clear ( ) ;
2019-03-30 16:46:50 +00:00
cp_array . clear ( ) ;
2018-11-17 18:24:02 +00:00
if ( ed = = 1 & & vid . stereo_mode = = sAnaglyph )
2018-02-03 12:41:49 +00:00
glClear ( GL_DEPTH_BUFFER_BIT ) ;
2019-10-21 20:34:20 +00:00
dynamicval < eModel > p ( pmodel , mdManual ) ;
2019-04-23 13:03:17 +00:00
current_display - > set_all ( ed ) ;
2018-07-21 22:39:57 +00:00
eyewidth_translate ( ed ) ;
2019-11-03 12:36:06 +00:00
if ( glhr : : current_glprogram - > uLevelLines ! = - 1 )
glUniform1f ( glhr : : current_glprogram - > uLevelLines , levellines ) ;
2018-11-17 18:24:02 +00:00
if ( vid . stereo_mode = = sODS ) {
2018-05-15 21:29:44 +00:00
glhr : : projection_multiply ( glhr : : ortho ( M_PI , M_PI , 100 ) ) ; // 2*M_PI));
2018-02-03 18:19:27 +00:00
}
2018-11-17 18:24:02 +00:00
else if ( rug_perspective | | current_display - > stereo_active ( ) ) {
2018-02-03 12:41:49 +00:00
2018-11-17 18:24:02 +00:00
xview = current_display - > tanfov ;
yview = current_display - > tanfov * vid . yres / vid . xres ;
2018-02-03 18:19:27 +00:00
2018-05-15 21:30:37 +00:00
glhr : : projection_multiply ( glhr : : frustum ( xview , yview , lowrug , hirug ) ) ;
2018-02-09 02:40:46 +00:00
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-11-17 18:24:02 +00:00
glhr : : projection_multiply ( glhr : : translate ( vid . 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 ;
2018-11-17 18:24:02 +00:00
push_point ( p - > precompute , 0 , vid . ipd * ed / 2 ) ;
2018-02-03 12:41:49 +00:00
}
}
}
}
else {
2018-11-17 18:24:02 +00:00
xview = current_display - > tanfov * model_distance ;
yview = current_display - > 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 (
2018-05-15 21:30:37 +00:00
no_fog ? 1000 :
2018-02-09 02:40:46 +00:00
gwhere = = gSphere & & rug_perspective ? 10 :
gwhere = = gElliptic & & rug_perspective ? 4 :
2019-06-13 15:56:25 +00:00
100 ,
darkena ( backcolor , 0 , 0xFF )
2018-02-09 00:46:14 +00:00
) ;
2019-11-03 12:36:06 +00:00
GLERR ( " fog_max " ) ;
2018-02-11 18:08:17 +00:00
2018-06-22 12:47:24 +00:00
for ( int t = 0 ; t < isize ( triangles ) ; t + + )
2016-08-26 09:58:03 +00:00
drawTriangle ( triangles [ t ] ) ;
2018-02-09 00:46:14 +00:00
2018-02-20 10:30:39 +00:00
glhr : : id_modelview ( ) ;
2019-03-30 16:46:50 +00:00
if ( isize ( ct_array ) > 0 ) {
glhr : : prepare ( ct_array ) ;
glDrawArrays ( GL_TRIANGLES , 0 , isize ( ct_array ) ) ;
}
2019-11-03 12:36:06 +00:00
GLERR ( " rugz " ) ;
2019-03-30 16:46:50 +00:00
if ( isize ( cp_array ) > 0 ) {
glhr : : prepare ( cp_array ) ;
glLineWidth ( lwidth ) ;
glDrawArrays ( GL_LINES , 0 , isize ( cp_array ) ) ;
}
2019-11-03 12:36:06 +00:00
GLERR ( " rugt " ) ;
2018-02-03 12:41:49 +00:00
2018-11-17 18:24:02 +00:00
current_display - > set_mask ( 0 ) ;
2019-11-03 12:36:06 +00:00
GLERR ( " afterrug " ) ;
2016-08-26 09:58:03 +00:00
}
glEnable ( GL_BLEND ) ;
2018-03-02 12:06:28 +00:00
if ( rug_failure ) {
rug : : close ( ) ;
2018-03-24 14:17:17 +00:00
rug : : clear_model ( ) ;
2018-03-02 12:06:28 +00:00
rug : : init ( ) ;
}
2016-08-26 09:58:03 +00:00
}
// organization
//--------------
2019-08-09 23:07:39 +00:00
EX transmatrix currentrot ;
2019-11-03 14:52:19 +00:00
EX void close_glbuf ( ) {
delete glbuf ;
glbuf = nullptr ;
}
EX void ensure_glbuf ( ) {
if ( glbuf ) return ;
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 " ) ) ;
2019-11-03 14:52:19 +00:00
close_glbuf ( ) ;
2018-02-03 12:50:47 +00:00
return ;
}
2019-11-03 14:52:19 +00:00
}
EX void reopen ( ) {
if ( rugged ) return ;
rugdim = 2 * GDIM - 1 ;
when_enabled = 0 ;
GLERR ( " before init " ) ;
ensure_glbuf ( ) ;
if ( ! glbuf ) { rugged = false ; return ; }
2018-02-03 12:50:47 +00:00
rugged = true ;
2016-08-26 09:58:03 +00:00
if ( renderonce ) prepareTexture ( ) ;
if ( ! rugged ) return ;
2018-03-24 14:17:17 +00:00
}
2019-08-09 23:07:39 +00:00
EX bool display_warning = true ;
2019-07-12 21:16:18 +00:00
2019-08-09 23:07:39 +00:00
EX void init_model ( ) {
2018-03-24 14:17:17 +00:00
clear_model ( ) ;
2016-08-26 09:58:03 +00:00
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
2019-02-27 18:34:18 +00:00
# if CAP_CRYSTAL
2019-08-22 10:14:39 +00:00
if ( cryst & & surface : : sh = = surface : : dsNone ) {
2018-12-02 19:26:58 +00:00
surface : : sh = surface : : dsCrystal ;
crystal : : init_rotation ( ) ;
good_shape = true ;
return ;
}
2019-02-27 18:34:18 +00:00
# endif
2018-12-02 19:26:58 +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 ;
2019-07-12 21:16:18 +00:00
if ( display_warning & & ! valid )
2017-12-29 13:31:18 +00:00
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 ( ) ;
2018-03-24 14:17:17 +00:00
clear_model ( ) ;
2017-12-29 11:54:50 +00:00
}
2016-08-26 09:58:03 +00:00
}
2019-08-09 23:07:39 +00:00
EX void init ( ) {
2019-05-28 23:09:38 +00:00
if ( dual : : state ) return ;
2018-03-24 14:17:17 +00:00
reopen ( ) ;
if ( rugged ) init_model ( ) ;
}
2019-08-09 23:07:39 +00:00
EX void clear_model ( ) {
2016-08-26 09:58:03 +00:00
triangles . clear ( ) ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( points ) ; i + + ) delete points [ i ] ;
2016-08-26 09:58:03 +00:00
points . clear ( ) ;
pqueue = queue < rugpoint * > ( ) ;
2018-03-24 14:17:17 +00:00
}
2019-08-09 23:07:39 +00:00
EX void close ( ) {
2018-03-24 14:17:17 +00:00
if ( ! rugged ) return ;
rugged = false ;
2019-11-03 14:52:19 +00:00
close_glbuf ( ) ;
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 ;
2019-08-09 23:07:39 +00:00
EX void apply_rotation ( const transmatrix & t ) {
2018-01-29 15:30:21 +00:00
if ( ! rug_perspective ) currentrot = t * currentrot ;
2019-02-27 18:34:18 +00:00
# if CAP_CRYSTAL
2018-11-30 19:29:14 +00:00
if ( in_crystal ( ) ) crystal : : apply_rotation ( t ) ;
2019-02-27 18:34:18 +00:00
else
# endif
for ( auto p : points ) p - > flat = t * p - > flat ;
2018-01-29 15:30:21 +00:00
}
2019-08-09 23:07:39 +00:00
EX 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 ) ;
}
2019-09-26 11:18:58 +00:00
# define CAP_HOLDKEYS (CAP_SDL && !ISWEB)
2018-02-11 18:08:17 +00:00
2019-08-09 23:07:39 +00:00
EX bool handlekeys ( int sym , int uni ) {
2018-12-06 11:31:51 +00:00
if ( NUMBERKEY = = ' 1 ' ) {
2018-01-29 15:30:21 +00:00
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 ( ) ;
}
}
2018-02-27 18:39:22 +00:00
if ( renderonce ) renderlate + = 10 ;
2018-01-29 15:30:21 +00:00
return true ;
}
2018-12-06 11:31:51 +00:00
else if ( NUMBERKEY = = ' 2 ' ) {
2019-02-27 18:34:18 +00:00
# if CAP_CRYSTAL
2018-11-30 19:29:14 +00:00
if ( in_crystal ( ) )
crystal : : switch_z_coordinate ( ) ;
else
2019-02-27 18:34:18 +00:00
# endif
2019-08-09 21:59:32 +00:00
apply_rotation ( cspin ( 0 , 2 , M_PI ) ) ;
2018-01-29 15:30:21 +00:00
return true ;
}
2018-12-06 11:31:51 +00:00
else if ( NUMBERKEY = = ' 3 ' ) {
2019-02-27 18:34:18 +00:00
# if CAP_CRYSTAL
2018-11-30 19:29:14 +00:00
if ( in_crystal ( ) )
2018-12-06 10:43:10 +00:00
crystal : : flip_z ( ) ;
2018-11-30 19:29:14 +00:00
else
2019-02-27 18:34:18 +00:00
# endif
2019-08-09 21:59:32 +00:00
apply_rotation ( cspin ( 0 , 2 , M_PI / 2 ) ) ;
2018-01-29 15:30:21 +00:00
return true ;
}
2019-02-27 18:34:18 +00:00
# if CAP_CRYSTAL
2018-12-06 10:43:10 +00:00
else if ( sym = = SDLK_HOME & & in_crystal ( ) ) {
crystal : : next_home_orientation ( ) ;
return true ;
}
2019-02-27 18:34:18 +00:00
# endif
2018-02-11 18:08:17 +00:00
# if !CAP_HOLDKEYS
2018-12-06 10:43:10 +00:00
else if ( sym = = SDLK_PAGEUP | | uni = = ' [ ' ) {
2018-02-04 00:04:29 +00:00
move_forward ( .1 ) ;
return true ;
}
2018-12-06 10:43:10 +00:00
else if ( sym = = SDLK_PAGEDOWN | | uni = = ' ] ' ) {
2018-02-04 00:04:29 +00:00
move_forward ( - .1 ) ;
return true ;
}
2019-08-09 21:59:32 +00:00
else if ( sym = = SDLK_HOME ) { apply_rotation ( cspin ( 0 , 1 , .1 ) ) ; return true ; }
else if ( sym = = SDLK_END ) { apply_rotation ( cspin ( 1 , 0 , .1 ) ) ; return true ; }
else if ( sym = = SDLK_DOWN ) { apply_rotation ( cspin ( 2 , 1 , .1 ) ) ; return true ; }
else if ( sym = = SDLK_UP ) { apply_rotation ( cspin ( 1 , 2 , .1 ) ) ; return true ; }
else if ( sym = = SDLK_LEFT ) { apply_rotation ( cspin ( 2 , 0 , .1 ) ) ; return true ; }
else if ( sym = = SDLK_RIGHT ) { apply_rotation ( cspin ( 0 , 2 , .1 ) ) ; return true ; }
2018-02-03 13:31:17 +00:00
# endif
2018-01-29 15:30:21 +00:00
else return false ;
}
2019-08-09 23:07:39 +00:00
EX void finger_on ( int coord , ld val ) {
2018-01-29 15:30:21 +00:00
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 ;
2019-08-09 23:07:39 +00:00
EX ld ruggospeed = 1 ;
2018-05-18 15:34:12 +00:00
2019-08-09 23:07:39 +00:00
EX 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-12 15:21:55 +00:00
else if ( renderlate ) {
renderlate - - ;
prepareTexture ( ) ;
}
2018-09-02 13:11:35 +00:00
// do not display button
else playerfound = true ;
2018-11-17 18:24:02 +00:00
current_display - > 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
2019-09-26 11:18:45 +00:00
if ( ! when_enabled ) ticks = when_enabled ;
2018-02-03 13:35:06 +00:00
if ( ticks < when_enabled + 500 )
last_orientation = getOrientation ( ) ;
else {
transmatrix next_orientation = getOrientation ( ) ;
2019-06-28 08:00:37 +00:00
apply_rotation ( inverse ( next_orientation ) * last_orientation ) ;
2018-02-03 13:35:06 +00:00
last_orientation = next_orientation ;
2018-02-03 18:19:27 +00:00
}
2018-02-03 13:35:06 +00:00
# endif
2018-02-12 11:49:47 +00:00
2016-08-26 09:58:03 +00:00
int qm = 0 ;
double alpha = ( ticks - lastticks ) / 1000.0 ;
lastticks = ticks ;
2018-02-12 11:49:47 +00:00
if ( ruggo ) move_forward ( ruggo * alpha ) ;
# if CAP_HOLDKEYS
Uint8 * keystate = SDL_GetKeyState ( NULL ) ;
2018-03-24 14:17:17 +00:00
if ( keystate [ SDLK_LALT ] ) alpha / = 10 ;
2018-02-12 11:49:47 +00:00
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 {
2019-08-09 21:59:32 +00:00
if ( keystate [ SDLK_HOME ] ) qm + + , t = t * cspin ( 0 , 1 , alpha ) , protractor + = alpha ;
if ( keystate [ SDLK_END ] ) qm + + , t = t * cspin ( 1 , 0 , alpha ) , protractor - = alpha ;
2018-01-29 15:30:21 +00:00
if ( ! keystate [ SDLK_LSHIFT ] ) {
2019-08-09 21:59:32 +00:00
if ( keystate [ SDLK_DOWN ] ) qm + + , t = t * cspin ( 2 , 1 , alpha ) , protractor + = alpha ;
if ( keystate [ SDLK_UP ] ) qm + + , t = t * cspin ( 1 , 2 , alpha ) , protractor - = alpha ;
if ( keystate [ SDLK_LEFT ] ) qm + + , t = t * cspin ( 2 , 0 , alpha ) , protractor + = alpha ;
if ( keystate [ SDLK_RIGHT ] ) qm + + , t = t * cspin ( 0 , 2 , alpha ) , protractor - = alpha ;
2018-01-29 15:30:21 +00:00
}
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 ;
2018-05-18 15:34:12 +00:00
push_all_points ( 2 , push * ruggospeed ) ;
push_all_points ( 0 , strafex * ruggospeed ) ;
push_all_points ( 1 , strafey * ruggospeed ) ;
2017-12-25 22:47:57 +00:00
}
else {
2018-01-29 15:30:21 +00:00
if ( finger_center )
perform_finger ( ) ;
else {
2018-11-30 19:29:14 +00:00
if ( keystate [ SDLK_HOME ] & & ! in_crystal ( ) ) qm + + , t = inverse ( currentrot ) ;
if ( keystate [ SDLK_END ] ) {
qm + + ;
2019-08-09 21:59:32 +00:00
if ( in_crystal ( ) ) t = t * cspin ( 0 , 1 , alpha ) ;
else t = currentrot * cspin ( 0 , 1 , alpha ) * inverse ( currentrot ) ;
2018-11-30 19:29:14 +00:00
}
2019-08-09 21:59:32 +00:00
if ( keystate [ SDLK_DOWN ] ) qm + + , t = t * cspin ( 1 , 2 , alpha ) ;
if ( keystate [ SDLK_UP ] ) qm + + , t = t * cspin ( 2 , 1 , alpha ) ;
if ( keystate [ SDLK_LEFT ] ) qm + + , t = t * cspin ( 0 , 2 , alpha ) ;
if ( keystate [ SDLK_RIGHT ] ) qm + + , t = t * cspin ( 2 , 0 , alpha ) ;
2018-05-18 15:34:12 +00:00
if ( keystate [ SDLK_PAGEUP ] ) model_distance / = exp ( alpha * ruggospeed ) ;
if ( keystate [ SDLK_PAGEDOWN ] ) model_distance * = exp ( alpha * ruggospeed ) ;
2017-12-25 22:47:57 +00:00
}
2018-01-29 15:30:21 +00:00
2018-03-24 14:17:17 +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 ;
2019-08-09 23:07:39 +00:00
EX hyperpoint gethyper ( ld x , ld y ) {
2018-11-17 18:24:02 +00:00
double mx = ( x - current_display - > xcenter ) / vid . xres * 2 * xview ;
double my = ( current_display - > 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 ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( triangles ) ; i + + ) {
2017-11-06 18:24:02 +00:00
auto r0 = triangles [ i ] . m [ 0 ] ;
auto r1 = triangles [ i ] . m [ 1 ] ;
auto r2 = triangles [ i ] . m [ 2 ] ;
2019-03-30 16:46:50 +00:00
if ( ! r2 ) continue ;
2019-04-29 01:38:27 +00:00
if ( ! r0 - > valid | | ! r1 - > valid | | ! r2 - > valid ) continue ;
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
2018-11-20 18:01:10 +00:00
calcparam_rug ( ) ;
2019-08-09 22:58:50 +00:00
models : : configure ( ) ;
2018-06-10 23:58:31 +00:00
hyperpoint h = hr : : gethyper ( px , py ) ;
2018-11-20 18:01:10 +00:00
calcparam ( ) ;
2016-08-26 09:58:03 +00:00
return h ;
}
2019-08-09 23:07:39 +00:00
EX string makehelp ( ) {
2018-02-26 12:15:33 +00:00
return
XLAT (
" 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 " */
# if !ISMOBILE
+ XLAT ( " Use arrow keys to rotate, Page Up/Down to zoom. " )
+ " \n \n " +
2018-03-02 12:06:28 +00:00
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. " )
# endif
;
2018-02-26 12:15:33 +00:00
}
2019-08-09 23:07:39 +00:00
EX string geometry_name ( eGeometry g ) {
2019-02-06 15:34:22 +00:00
switch ( g ) {
case gNormal : return XLAT ( " hyperbolic " ) ;
case gEuclid : return XLAT ( " Euclidean " ) ;
case gSphere : return XLAT ( " spherical " ) ;
case gElliptic : return XLAT ( " elliptic " ) ;
default : return XLAT ( " unknown " ) ;
}
}
2019-05-03 09:45:51 +00:00
void change_texturesize ( ) {
if ( rugged ) {
close ( ) ;
reopen ( ) ;
}
}
2019-05-03 09:46:14 +00:00
ld old_distance ;
2019-08-09 23:07:39 +00:00
EX void show ( ) {
2018-12-13 16:02:10 +00:00
cmode = sm : : SIDE | sm : : MAYDARK ;
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-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 )
2019-02-06 15:34:22 +00:00
dialog : : addSelItem ( XLAT ( " native geometry " ) , geometry_name ( gwhere ) , ' n ' ) ;
2017-12-27 14:25:10 +00:00
else
2019-05-21 22:01:30 +00:00
dialog : : addSelItem ( XLAT ( " radar " ) , radar_distance = = RADAR_INF ? " ∞ " : fts ( radar_distance , 4 ) , ' 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 ) {
2019-05-21 22:01:30 +00:00
dialog : : addSelItem ( XLAT ( " maximum error " ) , fts ( err_zero ) , ' e ' ) ;
2017-12-27 14:25:10 +00:00
if ( rug : : rugged )
2019-05-21 22:01:30 +00:00
dialog : : lastItem ( ) . value + = " ( " + fts ( err_zero_current ) + " ) " ;
2017-11-07 13:39:26 +00:00
}
2018-02-27 18:39:46 +00:00
dialog : : addSelItem ( XLAT ( " automatic move speed " ) , fts ( ruggo ) , ' G ' ) ;
dialog : : addSelItem ( XLAT ( " anti-crossing " ) , fts ( anticusp_factor ) , ' A ' ) ;
2018-12-05 18:57:35 +00:00
dialog : : addBoolItem ( XLAT ( " 3D monsters/walls on the surface " ) , spatial_rug , ' S ' ) ;
dialog : : add_action ( [ ] ( ) { spatial_rug = ! spatial_rug ; } ) ;
2019-11-03 12:36:06 +00:00
edit_levellines ( ' L ' ) ;
2017-12-27 14:25:10 +00:00
2018-03-24 16:21:25 +00:00
# if CAP_SURFACE
if ( hyperbolic ) {
if ( gwhere = = gEuclid )
dialog : : addItem ( XLAT ( " smooth surfaces " ) , ' c ' ) ;
else dialog : : addBreak ( 100 ) ;
}
# endif
2018-06-12 22:11:26 +00:00
dialog : : addBreak ( 50 ) ;
dialog : : addHelp ( ) ;
dialog : : addBack ( ) ;
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
2018-06-12 22:11:26 +00:00
if ( uni = = ' h ' | | uni = = SDLK_F1 ) gotoHelp ( makehelp ( ) ) ;
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 ( ) ;
2018-03-24 16:21:25 +00:00
else {
# if CAP_SURFACE
surface : : sh = surface : : dsNone ;
# endif
rug : : init ( ) ;
}
2017-07-10 18:47:38 +00:00
}
2018-01-29 15:30:21 +00:00
else if ( uni = = ' R ' )
2018-02-13 12:37:20 +00:00
dialog : : editNumber ( finger_range , 0 , 1 , .01 , .1 , XLAT ( " finger range " ) ,
XLAT ( " Press 1 to enable the finger mode. " )
2018-01-29 15:30:21 +00:00
) ;
else if ( uni = = ' F ' )
2018-02-13 12:37:20 +00:00
dialog : : editNumber ( finger_force , 0 , 1 , .01 , .1 , XLAT ( " finger force " ) ,
XLAT ( " Press 1 to enable the finger force. " )
2018-01-29 15:30:21 +00:00
) ;
2018-03-24 16:21:25 +00:00
else if ( uni = = ' o ' )
2017-07-10 18:47:38 +00:00
renderonce = ! renderonce ;
2018-02-27 18:39:46 +00:00
else if ( uni = = ' G ' ) {
dialog : : editNumber ( ruggo , - 1 , 1 , .1 , 0 , XLAT ( " automatic move speed " ) ,
XLAT ( " Move automatically without pressing any keys. " )
) ;
}
else if ( uni = = ' A ' ) {
2018-02-27 18:47:35 +00:00
dialog : : editNumber ( anticusp_factor , 0 , 1.5 , .1 , 0 , XLAT ( " anti-crossing " ) ,
2018-02-27 18:39:46 +00:00
XLAT ( " The anti-crossing algorithm prevents the model from crossing itself, "
" by preventing points which should not be close from being close. "
" The bigger number, the more sensitive it is, but the embedding is slower. Set 0 to disable. " )
) ;
}
2017-12-27 14:25:10 +00:00
else if ( uni = = ' v ' ) {
2018-02-13 12:37:20 +00:00
dialog : : editNumber ( vertex_limit , 0 , 50000 , 500 , 3000 , ( " vertex limit " ) ,
XLAT ( " The more vertices, the more accurate the Hypersian Rug model is. "
" 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 ' ) {
2019-05-03 09:46:14 +00:00
dialog : : editNumber ( modelscale , 0.1 , 10 , rugged ? .01 : .1 , 1 , XLAT ( " model scale factor " ) ,
2018-02-13 12:37:20 +00:00
XLAT ( " 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; "
2018-02-13 12:37:20 +00:00
" if the native geometry is hyperbolic, and scale > 1, a hyperbolic plane will be rendered as an equidistant surface. " )
2017-12-27 14:25:10 +00:00
) ;
dialog : : scaleLog ( ) ;
2018-01-28 11:21:10 +00:00
if ( rug : : rugged ) {
2019-05-03 09:46:14 +00:00
static bool adjust_points = true ;
static bool camera_center = false ;
static bool adjust_edges = true ;
static bool adjust_distance = true ;
2018-01-28 11:21:10 +00:00
static ld last ;
last = modelscale ;
2019-05-03 09:46:14 +00:00
dialog : : extra_options = [ ] ( ) {
dialog : : addBoolItem_action ( XLAT ( " adjust points " ) , adjust_points , ' P ' ) ;
if ( adjust_points )
dialog : : addBoolItem_action ( XLAT ( " center on camera " ) , camera_center , ' C ' ) ;
else
dialog : : addBreak ( 100 ) ;
dialog : : addBoolItem_action ( XLAT ( " adjust edges " ) , adjust_edges , ' E ' ) ;
dialog : : addBoolItem_action ( XLAT ( " adjust distance " ) , adjust_distance , ' D ' ) ;
} ;
2018-01-28 11:21:10 +00:00
dialog : : reaction = [ ] ( ) {
2019-05-03 09:46:14 +00:00
if ( ! last | | ! modelscale ) return ;
if ( ! camera_center ) push_all_points ( 2 , model_distance ) ;
2018-01-28 11:21:10 +00:00
for ( auto p : points ) {
2019-05-03 09:46:14 +00:00
if ( adjust_edges ) for ( auto & e : p - > edges ) e . len * = modelscale / last ;
if ( adjust_points ) p - > flat * = modelscale / last ;
2018-01-28 11:21:10 +00:00
enqueue ( p ) ;
}
2019-05-03 09:46:14 +00:00
if ( adjust_distance ) model_distance = model_distance * modelscale / last ;
2018-01-28 11:21:10 +00:00
last = modelscale ;
2018-01-28 11:25:56 +00:00
good_shape = false ;
2019-05-03 09:46:14 +00:00
if ( ! camera_center ) push_all_points ( 2 , - model_distance ) ;
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 ) ;
}
}
2019-05-03 09:46:14 +00:00
else if ( uni = = ' d ' ) {
2018-02-13 12:37:20 +00:00
dialog : : editNumber ( model_distance , - 10 , 10 , .1 , 1 , XLAT ( " model distance " ) ,
XLAT ( " 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. " )
2018-01-03 01:04:34 +00:00
) ;
2019-05-03 09:46:14 +00:00
old_distance = model_distance ;
dialog : : reaction = [ ] ( ) {
if ( rug : : rugged & & rug_perspective ) {
push_all_points ( 2 , old_distance - model_distance ) ;
}
old_distance = model_distance ;
} ;
}
2017-12-27 14:25:10 +00:00
else if ( uni = = ' e ' ) {
2018-02-13 12:37:20 +00:00
dialog : : editNumber ( err_zero , 1e-9 , 1 , .1 , 1e-3 , XLAT ( " maximum error " ) ,
XLAT ( " New points are added when the current error in the model is smaller than this value. " )
2018-01-03 10:22:37 +00:00
) ;
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 ;
2019-05-03 09:45:51 +00:00
else if ( uni = = ' s ' ) {
2017-07-10 18:47:38 +00:00
texturesize * = 2 ;
2018-02-03 12:50:47 +00:00
if ( texturesize = = 8192 ) texturesize = 64 ;
2019-05-03 09:45:51 +00:00
change_texturesize ( ) ;
2017-07-10 18:47:38 +00:00
}
2018-03-24 16:21:25 +00:00
# if CAP_SURFACE
else if ( uni = = ' c ' )
pushScreen ( surface : : show_surfaces ) ;
# endif
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
}
2019-08-09 23:07:39 +00:00
EX void select ( ) {
2019-05-28 23:09:38 +00:00
if ( dual : : state ) return ;
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 " ) ) {
2018-11-09 13:26:31 +00:00
shift_arg_formula ( modelscale ) ;
2017-12-25 22:47:57 +00:00
}
else if ( argis ( " -ruggeo " ) ) {
shift ( ) ; gwhere = ( eGeometry ) argi ( ) ;
}
else if ( argis ( " -rugpers " ) ) {
rug_perspective = true ;
}
2018-02-12 15:21:55 +00:00
else if ( argis ( " -rugonce " ) ) {
renderonce = true ;
}
2018-04-09 13:56:23 +00:00
else if ( argis ( " -rugdist " ) ) {
2018-11-09 13:26:31 +00:00
shift_arg_formula ( model_distance ) ;
2018-04-09 13:56:23 +00:00
}
2018-02-12 15:21:55 +00:00
else if ( argis ( " -ruglate " ) ) {
2018-02-27 18:39:22 +00:00
renderonce = true ;
2018-02-12 15:21:55 +00:00
renderlate + = 10 ;
}
else if ( argis ( " -rugmany " ) ) {
renderonce = false ;
}
2019-03-30 16:46:50 +00:00
else if ( argis ( " -ruglwidth " ) ) {
shift_arg_formula ( lwidth ) ;
}
2018-02-12 11:49:47 +00:00
else if ( argis ( " -rugauto " ) ) {
2018-11-09 13:26:31 +00:00
shift_arg_formula ( ruggo ) ;
2018-02-12 11:49:47 +00:00
}
2017-12-25 22:47:57 +00:00
else if ( argis ( " -rugorth " ) ) {
rug_perspective = false ;
}
else if ( argis ( " -rugerr " ) ) {
2018-11-09 13:26:31 +00:00
shift_arg_formula ( err_zero ) ;
2017-12-27 09:52:54 +00:00
}
2018-01-28 11:21:29 +00:00
else if ( argis ( " -rugtsize " ) ) {
shift ( ) ; rug : : texturesize = argi ( ) ;
2019-05-03 09:45:51 +00:00
change_texturesize ( ) ;
2018-01-28 11:21:29 +00:00
}
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 " ) ) {
2018-02-12 11:49:47 +00:00
PHASE ( 3 ) ;
calcparam ( ) ;
rug : : init ( ) ;
2018-02-03 12:41:49 +00:00
}
2018-02-27 18:27:20 +00:00
else if ( argis ( " -sdfoff " ) ) {
subdivide_first = false ;
2018-01-29 15:31:14 +00:00
}
2018-02-27 18:27:20 +00:00
else if ( argis ( " -sdfon " ) ) {
subdivide_first = true ;
2018-01-28 11:20:21 +00:00
}
2018-02-27 18:39:54 +00:00
else if ( argis ( " -anticusp " ) ) {
2018-11-09 13:26:31 +00:00
shift_arg_formula ( anticusp_factor ) ;
2018-02-27 18:39:54 +00:00
}
2018-11-11 10:06:32 +00:00
else if ( argis ( " -d:rug " ) )
launch_dialog ( show ) ;
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
2018-06-10 23:58:31 +00:00
}