2019-12-14 10:29:53 +00:00
// Hyperbolic Rogue -- Arbitrary Tilings
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
/** \file arbitrile.cpp
* \ brief Arbitrary tilings
*
* Arbitrary tilings , defined in . tes files .
*/
# include "hyper.h"
namespace hr {
EX namespace arb {
2020-05-15 21:46:30 +00:00
EX int affine_limit = 200 ;
2020-06-06 16:41:36 +00:00
EX bool legacy ; /* angleofs command */
2019-12-14 10:29:53 +00:00
# if HDR
struct shape {
int id ;
2019-12-27 21:59:50 +00:00
int flags ;
2019-12-14 10:29:53 +00:00
vector < hyperpoint > vertices ;
vector < ld > angles ;
vector < ld > edges ;
vector < tuple < int , int , int > > connections ;
2019-12-23 20:49:54 +00:00
int size ( ) const { return isize ( vertices ) ; }
2019-12-14 10:29:53 +00:00
void build_from_angles_edges ( ) ;
2019-12-27 22:34:41 +00:00
vector < pair < int , int > > sublines ;
2020-05-15 21:46:30 +00:00
vector < pair < ld , ld > > stretch_shear ;
2020-05-31 01:24:41 +00:00
int repeat_value ;
2019-12-14 10:29:53 +00:00
} ;
2020-06-02 00:29:31 +00:00
struct slider {
string name ;
ld zero ;
ld current ;
ld min ;
ld max ;
} ;
2019-12-14 10:29:53 +00:00
struct arbi_tiling {
2019-12-27 21:59:02 +00:00
int order ;
2019-12-27 21:59:50 +00:00
bool have_line , have_ph ;
2019-12-27 21:59:02 +00:00
2019-12-14 10:29:53 +00:00
vector < shape > shapes ;
2019-12-27 00:43:58 +00:00
string name ;
2019-12-27 01:07:44 +00:00
string comment ;
2020-05-30 18:00:09 +00:00
2020-06-02 00:29:31 +00:00
vector < slider > sliders ;
2020-05-30 18:00:09 +00:00
ld cscale ;
2020-05-30 18:00:44 +00:00
string filename ;
2019-12-14 10:29:53 +00:00
geometryinfo1 & get_geometry ( ) ;
eGeometryClass get_class ( ) { return get_geometry ( ) . kind ; }
ld scale ( ) ;
} ;
# endif
EX arbi_tiling current ;
2020-06-02 00:29:31 +00:00
EX bool using_slided ;
EX arbi_tiling slided ;
2020-06-03 09:43:35 +00:00
EX bool in_slided ( ) { return in ( ) & & using_slided ; }
2020-06-02 00:29:31 +00:00
EX arbi_tiling & current_or_slided ( ) {
return using_slided ? slided : current ;
}
2019-12-14 10:29:53 +00:00
/** id of vertex in the arbitrary tiling */
EX short & id_of ( heptagon * h ) { return h - > zebraval ; }
2020-06-06 16:40:08 +00:00
# if HDR
2020-04-26 09:02:03 +00:00
struct hr_polygon_error : hr_exception {
vector < transmatrix > v ;
eGeometryClass c ;
int id ;
2020-05-30 18:06:28 +00:00
transmatrix end ;
2020-05-01 15:12:13 +00:00
map < string , cld > params ;
2020-05-30 18:06:28 +00:00
hr_polygon_error ( const vector < transmatrix > & _v , int _id , transmatrix _e ) : v ( _v ) , c ( cgclass ) , id ( _id ) , end ( _e ) { }
2020-04-26 09:02:03 +00:00
~ hr_polygon_error ( ) noexcept ( true ) { }
2020-06-06 16:40:08 +00:00
string generate_error ( ) ;
2020-04-26 09:02:03 +00:00
} ;
2020-06-06 16:40:08 +00:00
# endif
string hr_polygon_error : : generate_error ( ) {
cld dist = ( hdist0 ( tC0 ( end ) ) / params [ " distunit " ] ) ;
bool angle = abs ( dist ) < 1e-9 ;
if ( angle ) dist = ( atan2 ( end * xpush0 ( 1 ) ) / params [ " angleunit " ] ) ;
return
XLAT ( " Polygon number %1 did not close correctly (%2 %3). Here is the picture to help you understand the issue. \n \n " , its ( id ) ,
angle ? " angle " : " distance " ,
lalign ( 0 , dist )
) ;
}
2020-04-26 09:02:03 +00:00
2020-05-01 16:11:48 +00:00
struct connection_debug_request : hr_exception {
int id ;
eGeometryClass c ;
connection_debug_request ( int i ) : id ( i ) , c ( cgclass ) { }
} ;
void ensure_geometry ( eGeometryClass c ) {
2020-04-26 09:02:03 +00:00
stop_game ( ) ;
2020-05-01 16:11:48 +00:00
if ( c ! = cgclass ) {
if ( c = = gcEuclid ) set_geometry ( gEuclid ) ;
if ( c = = gcHyperbolic ) set_geometry ( gNormal ) ;
if ( c = = gcSphere ) set_geometry ( gSphere ) ;
2020-04-26 09:02:03 +00:00
}
if ( specialland ! = laCanvas ) {
canvas_default_wall = waInvisibleFloor ;
patterns : : whichCanvas = ' g ' ;
patterns : : canvasback = 0xFFFFFF ;
firstland = specialland = laCanvas ;
}
start_game ( ) ;
2020-05-01 16:11:48 +00:00
}
void start_poly_debugger ( hr_polygon_error & err ) {
2020-05-22 14:52:02 +00:00
# if CAP_EDIT
2020-05-01 16:11:48 +00:00
ensure_geometry ( err . c ) ;
2020-04-26 09:02:03 +00:00
drawthemap ( ) ;
mapeditor : : drawing_tool = true ;
pushScreen ( mapeditor : : showDrawEditor ) ;
mapeditor : : initdraw ( cwt . at ) ;
int n = isize ( err . v ) ;
mapeditor : : dtcolor = 0xFF0000FF ;
mapeditor : : dtwidth = 0.02 ;
for ( int i = 0 ; i < n - 1 ; i + + )
2020-07-27 16:49:04 +00:00
mapeditor : : dt_add_line ( shiftless ( tC0 ( err . v [ i ] ) ) , shiftless ( tC0 ( err . v [ i + 1 ] ) ) , 0 ) ;
2020-04-26 09:02:03 +00:00
mapeditor : : dtcolor = 0xFFFFFFFF ;
for ( int i = 0 ; i < n ; i + + )
2020-07-27 16:49:04 +00:00
mapeditor : : dt_add_text ( shiftless ( tC0 ( err . v [ i ] ) ) , 0.5 , its ( i ) ) ;
2020-05-22 14:52:02 +00:00
# endif
2020-04-26 09:02:03 +00:00
}
2019-12-14 10:29:53 +00:00
void shape : : build_from_angles_edges ( ) {
2019-12-26 21:28:46 +00:00
transmatrix at = Id ;
2019-12-14 10:29:53 +00:00
vertices . clear ( ) ;
int n = isize ( angles ) ;
hyperpoint ctr = Hypc ;
2020-04-26 09:02:03 +00:00
vector < transmatrix > matrices ;
2020-06-06 16:41:36 +00:00
if ( ! legacy ) for ( auto & a : angles ) a + = M_PI ;
2019-12-14 10:29:53 +00:00
for ( int i = 0 ; i < n ; i + + ) {
2020-04-26 09:02:03 +00:00
matrices . push_back ( at ) ;
2020-06-06 16:42:05 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , " at = " , at ) ;
2019-12-26 21:28:46 +00:00
vertices . push_back ( tC0 ( at ) ) ;
ctr + = tC0 ( at ) ;
at = at * xpush ( edges [ i ] ) * spin ( angles [ i ] ) ;
2019-12-14 10:29:53 +00:00
}
2020-04-26 09:02:03 +00:00
matrices . push_back ( at ) ;
2020-05-30 18:06:28 +00:00
if ( ! eqmatrix ( at , Id ) ) {
throw hr_polygon_error ( matrices , id , at ) ;
}
2020-03-22 09:15:03 +00:00
if ( sqhypot_d ( 3 , ctr ) < 1e-2 ) {
// this may happen for some spherical tilings
// try to move towards the center
2020-06-06 16:42:05 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , " special case encountered " ) ;
2020-03-22 09:15:03 +00:00
for ( int i = 0 ; i < n ; i + + ) {
ctr + = at * xpush ( edges [ i ] ) * spin ( ( angles [ i ] + M_PI ) / 2 ) * xpush0 ( .01 ) ;
at = at * xpush ( edges [ i ] ) * spin ( angles [ i ] ) ;
}
2020-06-06 16:42:05 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , " ctr = " , ctr ) ;
2020-03-22 09:15:03 +00:00
}
2020-06-06 16:41:36 +00:00
if ( ! legacy ) for ( auto & a : angles ) a - = M_PI ;
2019-12-14 10:29:53 +00:00
ctr = normalize ( ctr ) ;
2019-12-26 21:28:46 +00:00
for ( auto & v : vertices ) v = gpushxto0 ( ctr ) * v ;
2019-12-14 10:29:53 +00:00
}
2019-12-23 20:49:54 +00:00
bool correct_index ( int index , int size ) { return index > = 0 & & index < size ; }
template < class T > bool correct_index ( int index , const T & v ) { return correct_index ( index , isize ( v ) ) ; }
2020-04-16 18:59:34 +00:00
template < class T > void verify_index ( int index , const T & v , exp_parser & ep ) { if ( ! correct_index ( index , v ) ) throw hr_parse_exception ( " bad index: " + its ( index ) + " at " + ep . where ( ) ) ; }
2019-12-23 20:49:54 +00:00
2019-12-27 00:43:58 +00:00
string unnamed = " unnamed " ;
2020-06-02 00:29:31 +00:00
EX void load_tile ( exp_parser & ep , arbi_tiling & c , bool unit ) {
c . shapes . emplace_back ( ) ;
auto & cc = c . shapes . back ( ) ;
cc . id = isize ( c . shapes ) - 1 ;
2020-05-01 15:10:53 +00:00
cc . flags = 0 ;
2020-05-31 01:24:41 +00:00
cc . repeat_value = 1 ;
2020-05-01 15:10:53 +00:00
while ( ep . next ( ) ! = ' ) ' ) {
2020-05-01 15:14:49 +00:00
cld dist = 1 ;
2020-05-01 15:10:53 +00:00
if ( ! unit ) {
2020-05-01 15:14:49 +00:00
dist = ep . parse ( 0 ) ;
2020-05-01 15:10:53 +00:00
ep . force_eat ( " , " ) ;
}
2020-05-01 15:14:49 +00:00
cld angle = ep . parse ( 0 ) ;
2020-05-01 15:27:06 +00:00
cc . edges . push_back ( ep . validate_real ( dist * ep . extra_params [ " distunit " ] ) ) ;
cc . angles . push_back ( ep . validate_real ( angle * ep . extra_params [ " angleunit " ] + ep . extra_params [ " angleofs " ] ) ) ;
2020-05-01 15:10:53 +00:00
if ( ep . eat ( " , " ) ) continue ;
else if ( ep . eat ( " ) " )) break ;
else throw hr_parse_exception ( " expecting , or ) " ) ;
}
try {
cc . build_from_angles_edges ( ) ;
}
catch ( hr_parse_exception & ex ) {
throw hr_parse_exception ( ex . s + ep . where ( ) ) ;
}
2020-05-01 15:12:13 +00:00
catch ( hr_polygon_error & poly ) {
poly . params = ep . extra_params ;
throw ;
}
2020-05-01 15:10:53 +00:00
cc . connections . resize ( cc . size ( ) ) ;
for ( int i = 0 ; i < isize ( cc . connections ) ; i + + )
cc . connections [ i ] = make_tuple ( cc . id , i , false ) ;
2020-05-15 21:46:30 +00:00
cc . stretch_shear . resize ( cc . size ( ) , make_pair ( 1 , 0 ) ) ;
2020-05-01 15:10:53 +00:00
}
2020-06-02 00:29:31 +00:00
EX void load ( const string & fname , bool after_sliding IS ( false ) ) {
2019-12-14 10:29:53 +00:00
fhstream f ( fname , " rt " ) ;
2020-06-03 09:43:48 +00:00
if ( ! f . f ) throw hr_parse_exception ( " file " + fname + " does not exist " ) ;
2019-12-23 20:49:54 +00:00
string s ;
while ( true ) {
int c = fgetc ( f . f ) ;
if ( c < 0 ) break ;
s + = c ;
}
2020-06-02 00:29:31 +00:00
auto & c = after_sliding ? slided : current ;
2019-12-27 21:59:02 +00:00
c . order + + ;
2019-12-14 10:29:53 +00:00
c . shapes . clear ( ) ;
2020-06-02 00:29:31 +00:00
c . sliders . clear ( ) ;
2019-12-27 00:43:58 +00:00
c . name = unnamed ;
2019-12-27 01:07:44 +00:00
c . comment = " " ;
2020-05-30 18:00:44 +00:00
c . filename = fname ;
2020-05-30 18:00:09 +00:00
c . cscale = 1 ;
2019-12-23 20:49:54 +00:00
exp_parser ep ;
ep . s = s ;
2019-12-26 21:28:46 +00:00
ld angleunit = 1 , distunit = 1 , angleofs = 0 ;
2019-12-27 21:59:50 +00:00
auto addflag = [ & ] ( int f ) {
int ai ;
if ( ep . next ( ) = = ' ) ' ) ai = isize ( c . shapes ) - 1 ;
else ai = ep . iparse ( ) ;
2020-04-16 18:59:34 +00:00
verify_index ( ai , c . shapes , ep ) ;
2019-12-27 21:59:50 +00:00
c . shapes [ ai ] . flags | = f ;
ep . force_eat ( " ) " ) ;
} ;
2019-12-14 10:29:53 +00:00
while ( true ) {
2020-03-22 10:27:06 +00:00
ep . extra_params [ " distunit " ] = distunit ;
ep . extra_params [ " angleunit " ] = angleunit ;
ep . extra_params [ " angleofs " ] = angleofs ;
2019-12-23 20:49:54 +00:00
ep . skip_white ( ) ;
if ( ep . next ( ) = = 0 ) break ;
2019-12-27 00:43:58 +00:00
if ( ep . eat ( " # " ) ) {
2019-12-27 01:07:44 +00:00
bool doubled = ep . eat ( " # " ) ;
2019-12-27 00:43:58 +00:00
while ( ep . eat ( " " ) ) ;
string s = " " ;
while ( ep . next ( ) > = 32 ) s + = ep . next ( ) , ep . at + + ;
2019-12-27 01:07:44 +00:00
if ( doubled ) {
if ( c . name = = unnamed ) c . name = s ;
else {
c . comment + = s ;
c . comment + = " \n " ;
}
}
2019-12-27 00:43:58 +00:00
}
else if ( ep . eat ( " e2. " ) ) {
2020-05-15 21:46:30 +00:00
ginf [ gArbitrary ] . g = giEuclid2 ;
ginf [ gArbitrary ] . sides = 7 ;
set_flag ( ginf [ gArbitrary ] . flags , qBOUNDED , false ) ;
set_flag ( ginf [ gArbitrary ] . flags , qAFFINE , false ) ;
2020-06-03 14:42:13 +00:00
geom3 : : apply_always3 ( ) ;
2020-05-15 21:46:30 +00:00
}
else if ( ep . eat ( " a2. " ) ) {
2019-12-26 21:28:46 +00:00
ginf [ gArbitrary ] . g = giEuclid2 ;
2020-01-06 23:03:57 +00:00
ginf [ gArbitrary ] . sides = 7 ;
2019-12-26 21:28:46 +00:00
set_flag ( ginf [ gArbitrary ] . flags , qBOUNDED , false ) ;
2020-05-15 09:38:20 +00:00
set_flag ( ginf [ gArbitrary ] . flags , qAFFINE , true ) ;
2020-05-15 21:46:30 +00:00
affine_limit = 200 ;
2020-06-03 14:42:13 +00:00
geom3 : : apply_always3 ( ) ;
2019-12-26 21:28:46 +00:00
}
else if ( ep . eat ( " h2. " ) ) {
ginf [ gArbitrary ] . g = giHyperb2 ;
2020-01-06 23:03:57 +00:00
ginf [ gArbitrary ] . sides = 7 ;
2019-12-26 21:28:46 +00:00
set_flag ( ginf [ gArbitrary ] . flags , qBOUNDED , false ) ;
2020-05-15 09:38:20 +00:00
set_flag ( ginf [ gArbitrary ] . flags , qAFFINE , false ) ;
2020-06-03 14:42:13 +00:00
geom3 : : apply_always3 ( ) ;
2019-12-26 21:28:46 +00:00
}
else if ( ep . eat ( " s2. " ) ) {
ginf [ gArbitrary ] . g = giSphere2 ;
2020-01-06 23:03:57 +00:00
ginf [ gArbitrary ] . sides = 5 ;
2020-05-15 21:46:44 +00:00
set_flag ( ginf [ gArbitrary ] . flags , qBOUNDED , true ) ;
2020-05-15 09:38:20 +00:00
set_flag ( ginf [ gArbitrary ] . flags , qAFFINE , false ) ;
2020-06-03 14:42:13 +00:00
geom3 : : apply_always3 ( ) ;
2019-12-26 21:28:46 +00:00
}
2020-06-06 16:41:36 +00:00
else if ( ep . eat ( " legacysign. " ) ) {
if ( legacy ) angleunit * = - 1 ;
}
2019-12-26 21:28:46 +00:00
else if ( ep . eat ( " angleunit( " ) ) angleunit = real ( ep . parsepar ( ) ) ;
2020-06-06 16:41:36 +00:00
else if ( ep . eat ( " angleofs( " ) ) {
angleofs = real ( ep . parsepar ( ) ) ;
if ( ! legacy ) angleofs = 0 ;
}
2019-12-26 21:28:46 +00:00
else if ( ep . eat ( " distunit( " ) ) distunit = real ( ep . parsepar ( ) ) ;
2019-12-27 21:59:50 +00:00
else if ( ep . eat ( " line( " ) ) {
addflag ( arcm : : sfLINE ) ;
c . have_line = true ;
}
else if ( ep . eat ( " grave( " ) ) {
addflag ( arcm : : sfPH ) ;
c . have_ph = true ;
}
2020-06-02 00:29:31 +00:00
else if ( ep . eat ( " slider( " ) ) {
slider sl ;
sl . name = ep . next_token ( ) ;
ep . force_eat ( " , " ) ;
sl . current = sl . zero = ep . rparse ( ) ;
ep . force_eat ( " , " ) ;
sl . min = ep . rparse ( ) ;
ep . force_eat ( " , " ) ;
sl . max = ep . rparse ( ) ;
ep . force_eat ( " ) " ) ;
c . sliders . push_back ( sl ) ;
if ( after_sliding )
ep . extra_params [ sl . name ] = current . sliders [ isize ( c . sliders ) - 1 ] . current ;
else
ep . extra_params [ sl . name ] = sl . zero ;
}
2019-12-26 21:28:46 +00:00
else if ( ep . eat ( " let( " ) ) {
2019-12-23 20:49:54 +00:00
string tok = ep . next_token ( ) ;
ep . force_eat ( " = " ) ;
ep . extra_params [ tok ] = ep . parsepar ( ) ;
2020-04-29 13:10:07 +00:00
if ( debugflags & DF_GEOM )
2020-06-02 00:29:31 +00:00
println ( hlog , " let " , tok , " = " , ep . extra_params [ tok ] ) ;
2019-12-23 20:49:54 +00:00
}
2020-06-02 00:29:31 +00:00
else if ( ep . eat ( " unittile( " ) ) load_tile ( ep , c , true ) ;
else if ( ep . eat ( " tile( " ) ) load_tile ( ep , c , false ) ;
2020-05-15 21:46:30 +00:00
else if ( ep . eat ( " affine_limit( " ) ) {
affine_limit = ep . iparse ( ) ;
ep . force_eat ( " ) " ) ;
}
2020-05-30 18:00:09 +00:00
else if ( ep . eat ( " cscale( " ) ) {
c . cscale = ep . rparse ( ) ;
ep . force_eat ( " ) " ) ;
}
2019-12-27 22:00:07 +00:00
else if ( ep . eat ( " conway( \" " ) ) {
string s = " " ;
while ( true ) {
int m = 0 ;
if ( ep . eat ( " ( " ) ) m = 0 ;
else if ( ep . eat ( " [ " ) ) m = 1 ;
else if ( ep . eat ( " \" " ) ) break ;
2020-04-16 18:59:34 +00:00
else throw hr_parse_exception ( " cannot parse Conway notation, " + ep . where ( ) ) ;
2019-12-27 22:00:07 +00:00
int ai = 0 ;
int as = ep . iparse ( ) ;
while ( ep . eat ( " ' " ) ) ai + + ;
if ( ep . eat ( " @ " ) ) ai = ep . iparse ( ) ;
int bi = 0 , bs = 0 ;
if ( ep . eat ( " ) " ) | | ep . eat ( " ] " ) ) bs = as , bi = ai ;
else {
bs = ep . iparse ( ) ;
while ( ep . eat ( " ' " ) ) bi + + ;
if ( ep . eat ( " @ " ) ) bi = ep . iparse ( ) ;
}
if ( ep . eat ( " ) " ) | | ep . eat ( " ] " ) ) { }
2020-06-06 16:42:34 +00:00
verify_index ( ai , c . shapes , ep ) ;
verify_index ( as , c . shapes [ ai ] , ep ) ;
verify_index ( bi , c . shapes , ep ) ;
verify_index ( bs , c . shapes [ bi ] , ep ) ;
2019-12-27 22:00:07 +00:00
c . shapes [ ai ] . connections [ as ] = make_tuple ( bi , bs , m ) ;
c . shapes [ bi ] . connections [ bs ] = make_tuple ( ai , as , m ) ;
}
ep . force_eat ( " ) " ) ;
}
2019-12-26 21:28:46 +00:00
else if ( ep . eat ( " c( " ) ) {
2020-04-16 18:59:34 +00:00
int ai = ep . iparse ( ) ; verify_index ( ai , c . shapes , ep ) ; ep . force_eat ( " , " ) ;
int as = ep . iparse ( ) ; verify_index ( as , c . shapes [ ai ] , ep ) ; ep . force_eat ( " , " ) ;
int bi = ep . iparse ( ) ; verify_index ( bi , c . shapes , ep ) ; ep . force_eat ( " , " ) ;
int bs = ep . iparse ( ) ; verify_index ( bs , c . shapes [ bi ] , ep ) ; ep . force_eat ( " , " ) ;
2019-12-23 20:49:54 +00:00
int m = ep . iparse ( ) ; ep . force_eat ( " ) " ) ;
2019-12-27 12:11:31 +00:00
c . shapes [ ai ] . connections [ as ] = make_tuple ( bi , bs , m ) ;
c . shapes [ bi ] . connections [ bs ] = make_tuple ( ai , as , m ) ;
2019-12-23 20:49:54 +00:00
}
2019-12-27 22:34:41 +00:00
else if ( ep . eat ( " subline( " ) ) {
2020-04-16 18:59:34 +00:00
int ai = ep . iparse ( ) ; verify_index ( ai , c . shapes , ep ) ; ep . force_eat ( " , " ) ;
int as = ep . iparse ( ) ; verify_index ( as , c . shapes [ ai ] , ep ) ; ep . force_eat ( " , " ) ;
int bs = ep . iparse ( ) ; verify_index ( bs , c . shapes [ ai ] , ep ) ; ep . force_eat ( " ) " ) ;
2019-12-27 22:34:41 +00:00
c . shapes [ ai ] . sublines . emplace_back ( as , bs ) ;
}
else if ( ep . eat ( " sublines( " ) ) {
2020-01-02 15:40:30 +00:00
ld d = ep . rparse ( ) * distunit ;
ld eps = 1e-4 ;
if ( ep . eat ( " , " ) ) eps = ep . rparse ( ) * distunit ;
ep . force_eat ( " ) " ) ;
2019-12-27 22:34:41 +00:00
for ( auto & sh : c . shapes ) {
for ( int i = 0 ; i < isize ( sh . vertices ) ; i + + )
2020-06-06 16:42:43 +00:00
for ( int j = 0 ; j < i ; j + + )
2020-01-02 15:40:30 +00:00
if ( j ! = i + 1 & & i ! = j + 1 & & ! ( i = = 0 & & j = = isize ( sh . vertices ) - 1 ) & & ! ( j = = 0 & & i = = isize ( sh . vertices ) - 1 ) & & i ! = j ) {
ld dist = hdist ( sh . vertices [ i ] , sh . vertices [ j ] ) ;
if ( abs ( dist - d ) < eps ) {
2019-12-27 22:34:41 +00:00
sh . sublines . emplace_back ( i , j ) ;
2020-06-06 16:42:05 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , " add subline " , i , " - " , j ) ;
2019-12-27 22:34:41 +00:00
}
2020-01-02 15:40:30 +00:00
}
2019-12-27 22:34:41 +00:00
}
}
2020-05-31 01:24:41 +00:00
else if ( ep . eat ( " repeat( " ) ) {
int i = ep . iparse ( 0 ) ;
verify_index ( i , c . shapes , ep ) ;
ep . force_eat ( " , " ) ;
int rep = ep . iparse ( 0 ) ;
ep . force_eat ( " ) " ) ;
auto & sh = c . shapes [ i ] ;
int N = isize ( sh . angles ) ;
if ( N % rep )
throw hr_parse_exception ( " repeat value should be a factor of the number of vertices, " + ep . where ( ) ) ;
sh . repeat_value = rep ;
int d = N / rep ;
for ( int i = d ; i < N ; i + + )
sh . connections [ i ] = sh . connections [ i - d ] ;
}
2020-05-01 16:11:48 +00:00
else if ( ep . eat ( " debug( " ) ) {
int i = ep . iparse ( 0 ) ;
2020-05-01 16:31:04 +00:00
verify_index ( i , c . shapes , ep ) ;
2020-05-01 16:11:48 +00:00
ep . force_eat ( " ) " ) ;
throw connection_debug_request ( i ) ;
}
2020-05-15 21:46:30 +00:00
else if ( ep . eat ( " stretch_shear( " ) ) {
ld stretch = ep . rparse ( 0 ) ;
ep . force_eat ( " , " ) ;
ld shear = ep . rparse ( 0 ) ;
ep . force_eat ( " , " ) ;
int i = ep . iparse ( 0 ) ;
verify_index ( i , c . shapes , ep ) ;
ep . force_eat ( " , " ) ;
int j = ep . iparse ( 0 ) ;
verify_index ( j , c . shapes [ i ] , ep ) ;
ep . force_eat ( " ) " ) ;
auto & sh = c . shapes [ i ] ;
sh . stretch_shear [ j ] = { stretch , shear } ;
auto & co = sh . connections [ j ] ;
int i2 = get < 0 > ( co ) ;
int j2 = get < 1 > ( co ) ;
auto & xsh = c . shapes [ i2 ] ;
ld scale = sh . edges [ j ] / xsh . edges [ j2 ] ;
println ( hlog , " scale = " , scale ) ;
xsh . stretch_shear [ j2 ] = { 1 / stretch , shear * ( get < 2 > ( co ) ? 1 : - 1 ) * stretch } ;
}
2020-04-16 18:59:34 +00:00
else throw hr_parse_exception ( " expecting command, " + ep . where ( ) ) ;
2019-12-14 10:29:53 +00:00
}
2020-05-15 21:46:30 +00:00
if ( ! ( cgflags & qAFFINE ) ) {
for ( int i = 0 ; i < isize ( c . shapes ) ; i + + ) {
auto & sh = c . shapes [ i ] ;
for ( int j = 0 ; j < isize ( sh . edges ) ; j + + ) {
2020-05-16 13:34:56 +00:00
ld d1 = sh . edges [ j ] ;
2020-05-15 21:46:30 +00:00
auto con = sh . connections [ j ] ;
int i2 = get < 0 > ( con ) ;
int j2 = get < 1 > ( con ) ;
auto & xsh = c . shapes [ get < 0 > ( con ) ] ;
2020-05-16 13:34:56 +00:00
ld d2 = xsh . edges [ j2 ] ;
2020-05-15 21:46:30 +00:00
if ( abs ( d1 - d2 ) > 1e-6 )
throw hr_parse_exception ( lalign ( 0 , " connecting " , make_pair ( i , j ) , " to " , make_pair ( i2 , j2 ) , " of different lengths only possible in a2 " ) ) ;
}
}
}
2020-06-02 00:29:31 +00:00
if ( ! after_sliding ) slided = current ;
2019-12-14 10:29:53 +00:00
}
2020-05-01 16:11:48 +00:00
arbi_tiling debugged ;
vector < pair < transmatrix , int > > debug_polys ;
string primes ( int i ) {
string res ;
while ( i - - ) res + = " ' " ;
return res ;
}
void connection_debugger ( ) {
cmode = sm : : SIDE | sm : : DIALOG_STRICT_X ;
gamescreen ( 0 ) ;
auto & last = debug_polys . back ( ) ;
initquickqueue ( ) ;
for ( auto & p : debug_polys ) {
int id = p . second ;
2020-07-27 16:49:04 +00:00
shiftmatrix V = gmatrix [ cwt . at ] * p . first ;
2020-05-01 16:11:48 +00:00
auto & sh = debugged . shapes [ id ] . vertices ;
for ( auto & v : sh )
2020-07-27 16:49:04 +00:00
curvepoint ( v ) ;
2020-05-01 16:11:48 +00:00
2020-07-27 16:49:04 +00:00
curvepoint ( sh [ 0 ] ) ;
2020-05-01 16:11:48 +00:00
color_t col = colortables [ ' A ' ] [ id ] ;
col = darkena ( col , 0 , 0xFF ) ;
if ( & p = = & last ) {
vid . linewidth * = 2 ;
2020-07-27 16:49:04 +00:00
queuecurve ( V , 0xFFFF00FF , col , PPR : : LINE ) ;
2020-05-01 16:11:48 +00:00
vid . linewidth / = 2 ;
for ( int i = 0 ; i < isize ( sh ) ; i + + )
queuestr ( V * sh [ i ] , vid . fsize , its ( i ) , 0xFFFFFFFF ) ;
}
else
2020-07-27 16:49:04 +00:00
queuecurve ( V , 0xFFFFFFFF , col , PPR : : LINE ) ;
2020-05-01 16:11:48 +00:00
}
quickqueue ( ) ;
dialog : : init ( XLAT ( " connection debugger " ) ) ;
dialog : : addInfo ( debugged . name ) ;
dialog : : addHelp ( debugged . comment ) ;
dialog : : addBreak ( 50 ) ;
dialog : : addInfo ( " face index " + its ( last . second ) ) ;
dialog : : addBreak ( 50 ) ;
auto & sh = debugged . shapes [ last . second ] ;
int N = isize ( sh . edges ) ;
for ( int k = 0 ; k < N ; k + + ) {
auto con = sh . connections [ k ] ;
2020-05-01 17:35:50 +00:00
string cap = its ( k ) + primes ( last . second ) + " -> " + its ( get < 1 > ( con ) ) + primes ( get < 0 > ( con ) ) + ( get < 2 > ( con ) ? " (m) " : " " ) ;
2020-05-01 16:11:48 +00:00
dialog : : addSelItem ( cap , " go " , ' 0 ' + k ) ;
2020-05-03 19:11:49 +00:00
dialog : : add_action ( [ k , last , con ] {
2020-05-15 21:46:30 +00:00
if ( euclid ) cgflags | = qAFFINE ;
2020-05-01 16:11:48 +00:00
debug_polys . emplace_back ( last . first * get_adj ( debugged , last . second , k , - 1 ) , get < 0 > ( con ) ) ;
2020-05-15 21:46:30 +00:00
if ( euclid ) cgflags & = ~ qAFFINE ;
2020-05-01 16:11:48 +00:00
} ) ;
}
2020-07-08 10:42:10 +00:00
dialog : : addItem ( " undo " , ' u ' ) ;
dialog : : add_action ( [ ] {
if ( isize ( debug_polys ) > 1 )
debug_polys . pop_back ( ) ;
} ) ;
2020-05-01 16:11:48 +00:00
dialog : : addBack ( ) ;
dialog : : display ( ) ;
keyhandler = [ ] ( int sym , int uni ) {
handlePanning ( sym , uni ) ;
dialog : : handleNavigation ( sym , uni ) ;
if ( doexiton ( sym , uni ) ) popScreen ( ) ;
} ;
}
2019-12-14 10:29:53 +00:00
geometryinfo1 & arbi_tiling : : get_geometry ( ) {
return ginf [ gEuclid ] . g ;
}
map < heptagon * , vector < pair < heptagon * , transmatrix > > > altmap ;
EX map < heptagon * , pair < heptagon * , transmatrix > > arbi_matrix ;
EX hrmap * current_altmap ;
heptagon * build_child ( heptspin p , pair < int , int > adj ) ;
2020-05-01 16:11:48 +00:00
EX transmatrix get_adj ( arbi_tiling & c , int t , int dl , int xdl ) {
auto & sh = c . shapes [ t ] ;
int dr = gmod ( dl + 1 , sh . size ( ) ) ;
auto & co = sh . connections [ dl ] ;
int xt = get < 0 > ( co ) ;
if ( xdl = = - 1 ) xdl = get < 1 > ( co ) ;
int m = get < 2 > ( co ) ;
auto & xsh = c . shapes [ xt ] ;
int xdr = gmod ( xdl + 1 , xsh . size ( ) ) ;
hyperpoint vl = sh . vertices [ dl ] ;
hyperpoint vr = sh . vertices [ dr ] ;
hyperpoint vm = mid ( vl , vr ) ;
transmatrix rm = gpushxto0 ( vm ) ;
hyperpoint xvl = xsh . vertices [ xdl ] ;
hyperpoint xvr = xsh . vertices [ xdr ] ;
hyperpoint xvm = mid ( xvl , xvr ) ;
transmatrix xrm = gpushxto0 ( xvm ) ;
transmatrix Res = rgpushxto0 ( vm ) * rspintox ( rm * vr ) ;
2020-05-15 09:38:20 +00:00
if ( cgflags & qAFFINE ) {
ld sca = hdist ( vl , vr ) / hdist ( xvl , xvr ) ;
transmatrix Tsca = Id ;
Tsca [ 0 ] [ 0 ] = Tsca [ 1 ] [ 1 ] = sca ;
2020-05-15 21:46:30 +00:00
auto & ss = sh . stretch_shear [ dl ] ;
Tsca [ 0 ] [ 1 ] = ss . first * ss . second * sca ;
Tsca [ 1 ] [ 1 ] * = ss . first ;
2020-05-15 09:38:20 +00:00
Res = Res * Tsca ;
}
2020-05-01 16:11:48 +00:00
if ( m ) Res = Res * MirrorX ;
Res = Res * spintox ( xrm * xvl ) * xrm ;
if ( m ) swap ( vl , vr ) ;
if ( hdist ( vl , Res * xvr ) + hdist ( vr , Res * xvl ) > .1 ) {
println ( hlog , " s1 = " , kz ( spintox ( rm * vr ) ) , " s2 = " , kz ( rspintox ( xrm * xvr ) ) ) ;
println ( hlog , tie ( t , dl ) , " = " , kz ( Res ) ) ;
println ( hlog , hdist ( vl , Res * xvr ) , " # " , hdist ( vr , Res * xvl ) ) ;
exit ( 3 ) ;
}
return Res ;
}
2019-12-14 10:29:53 +00:00
struct hrmap_arbi : hrmap {
heptagon * origin ;
heptagon * getOrigin ( ) override { return origin ; }
hrmap_arbi ( ) {
dynamicval < hrmap * > curmap ( currentmap , this ) ;
origin = tailored_alloc < heptagon > ( current . shapes [ 0 ] . size ( ) ) ;
origin - > s = hsOrigin ;
origin - > emeraldval = 0 ;
origin - > zebraval = 0 ;
origin - > fiftyval = 0 ;
origin - > fieldval = 0 ;
origin - > rval0 = origin - > rval1 = 0 ;
origin - > cdata = NULL ;
origin - > alt = NULL ;
origin - > c7 = newCell ( origin - > type , origin ) ;
origin - > distance = 0 ;
heptagon * alt = NULL ;
if ( hyperbolic ) {
dynamicval < eGeometry > g ( geometry , gNormal ) ;
alt = tailored_alloc < heptagon > ( S7 ) ;
alt - > s = hsOrigin ;
alt - > emeraldval = 0 ;
alt - > zebraval = 0 ;
alt - > distance = 0 ;
alt - > c7 = NULL ;
alt - > alt = alt ;
alt - > cdata = NULL ;
current_altmap = newAltMap ( alt ) ;
}
transmatrix T = xpush ( .01241 ) * spin ( 1.4117 ) * xpush ( 0.1241 ) * Id ;
arbi_matrix [ origin ] = make_pair ( alt , T ) ;
altmap [ alt ] . emplace_back ( origin , T ) ;
cgi . base_distlimit = 0 ;
celllister cl ( origin - > c7 , 1000 , 200 , NULL ) ;
ginf [ geometry ] . distlimit [ 0 ] = cgi . base_distlimit = cl . dists . back ( ) ;
if ( sphere ) cgi . base_distlimit = SEE_ALL ;
}
~ hrmap_arbi ( ) {
/*
if ( hyperbolic ) for ( auto & p : arbi_matrix ) if ( p . second . first - > cdata ) {
delete p . second . first - > cdata ;
p . second . first - > cdata = NULL ;
}
*/
clearfrom ( origin ) ;
altmap . clear ( ) ;
arbi_matrix . clear ( ) ;
if ( current_altmap ) {
dynamicval < eGeometry > g ( geometry , gNormal ) ;
delete current_altmap ;
current_altmap = NULL ;
}
}
void verify ( ) override { }
transmatrix adj ( heptagon * h , int dl ) override {
2020-06-02 00:29:31 +00:00
return get_adj ( current_or_slided ( ) , id_of ( h ) , dl , h - > c . move ( dl ) ? h - > c . spin ( dl ) : - 1 ) ;
2019-12-14 10:29:53 +00:00
}
heptagon * create_step ( heptagon * h , int d ) override {
2020-06-02 00:29:31 +00:00
dynamicval < bool > sl ( using_slided , false ) ;
2019-12-14 10:29:53 +00:00
int t = id_of ( h ) ;
auto & sh = current . shapes [ t ] ;
auto & co = sh . connections [ d ] ;
int xt = get < 0 > ( co ) ;
int e = get < 1 > ( co ) ;
int m = get < 2 > ( co ) ;
2020-01-02 17:53:57 +00:00
auto & xsh = current . shapes [ xt ] ;
2019-12-14 10:29:53 +00:00
2020-05-15 09:38:20 +00:00
if ( cgflags & qAFFINE ) {
set < heptagon * > visited ;
vector < pair < heptagon * , transmatrix > > v ;
visited . insert ( h ) ;
v . emplace_back ( h , Id ) ;
transmatrix goal = adj ( h , d ) ;
2020-05-15 21:46:30 +00:00
for ( int i = 0 ; i < affine_limit & & i < isize ( v ) ; i + + ) {
2020-05-15 09:38:20 +00:00
transmatrix T = v [ i ] . second ;
heptagon * h2 = v [ i ] . first ;
if ( eqmatrix ( T , goal ) ) {
h - > c . connect ( d , h2 , e , m ) ;
return h2 ;
}
for ( int i = 0 ; i < h2 - > type ; i + + ) {
heptagon * h3 = h2 - > move ( i ) ;
if ( ! h3 ) continue ;
if ( visited . count ( h3 ) ) continue ;
visited . insert ( h3 ) ;
v . emplace_back ( h3 , T * adj ( h2 , i ) ) ;
}
}
auto h1 = tailored_alloc < heptagon > ( current . shapes [ xt ] . size ( ) ) ;
h1 - > distance = h - > distance + 1 ;
h1 - > zebraval = xt ;
h1 - > c7 = newCell ( h1 - > type , h1 ) ;
h1 - > alt = nullptr ;
h1 - > cdata = nullptr ;
h1 - > emeraldval = h - > emeraldval ^ m ;
h - > c . connect ( d , h1 , e , m ) ;
return h1 ;
}
const auto & p = arbi_matrix [ h ] ;
heptagon * alt = p . first ;
2019-12-14 10:29:53 +00:00
transmatrix T = p . second * adj ( h , d ) ;
if ( hyperbolic ) {
dynamicval < eGeometry > g ( geometry , gNormal ) ;
dynamicval < hrmap * > cm ( currentmap , current_altmap ) ;
// transmatrix U = T;
current_altmap - > virtualRebase ( alt , T ) ;
// U = U * inverse(T);
}
2020-01-15 16:54:54 +00:00
fixmatrix ( T ) ;
2019-12-14 10:29:53 +00:00
if ( euclid ) {
/* hash the rough coordinates as heptagon* alt */
size_t s = size_t ( T [ 0 ] [ LDIM ] + .261 ) * 124101 + size_t ( T [ 1 ] [ LDIM ] + .261 ) * 82143 ;
alt = ( heptagon * ) s ;
}
2020-01-02 17:53:57 +00:00
for ( auto & p2 : altmap [ alt ] ) if ( id_of ( p2 . first ) = = xt & & hdist ( tC0 ( p2 . second ) , tC0 ( T ) ) < 1e-2 ) {
2020-03-22 09:15:57 +00:00
for ( int oth = 0 ; oth < p2 . first - > type ; oth + + ) {
ld err = hdist ( p2 . second * xsh . vertices [ oth ] , T * xsh . vertices [ e ] ) ;
if ( err < 1e-2 ) {
2020-01-15 16:55:04 +00:00
static ld max_err = 0 ;
if ( err > max_err ) {
println ( hlog , " err = " , err ) ;
max_err = err ;
}
2020-01-02 17:53:57 +00:00
h - > c . connect ( d , p2 . first , oth % p2 . first - > type , m ) ;
return p2 . first ;
}
2020-03-22 09:15:57 +00:00
}
2019-12-14 10:29:53 +00:00
}
auto h1 = tailored_alloc < heptagon > ( current . shapes [ xt ] . size ( ) ) ;
h1 - > distance = h - > distance + 1 ;
h1 - > zebraval = xt ;
h1 - > c7 = newCell ( h1 - > type , h1 ) ;
h1 - > alt = nullptr ;
2019-12-26 21:28:46 +00:00
h1 - > cdata = nullptr ;
2020-01-02 17:55:17 +00:00
h1 - > emeraldval = h - > emeraldval ^ m ;
2019-12-14 10:29:53 +00:00
h - > c . connect ( d , h1 , e , m ) ;
arbi_matrix [ h1 ] = make_pair ( alt , T ) ;
altmap [ alt ] . emplace_back ( h1 , T ) ;
return h1 ;
}
2020-01-15 16:58:41 +00:00
transmatrix relative_matrix ( heptagon * h2 , heptagon * h1 , const hyperpoint & hint ) override {
2020-01-28 10:22:49 +00:00
return relative_matrix_recursive ( h2 , h1 ) ;
2020-01-15 16:58:41 +00:00
}
2019-12-14 10:29:53 +00:00
transmatrix adj ( cell * c , int dir ) override { return adj ( c - > master , dir ) ; }
ld spin_angle ( cell * c , int d ) override { return SPIN_NOT_AVAILABLE ; }
} ;
EX hrmap * new_map ( ) { return new hrmap_arbi ; }
2020-05-30 18:00:44 +00:00
EX void run ( string fname ) {
2020-04-26 09:02:03 +00:00
stop_game ( ) ;
eGeometry g = geometry ;
arbi_tiling t = current ;
auto v = variation ;
set_geometry ( gArbitrary ) ;
try {
load ( fname ) ;
ginf [ gArbitrary ] . tiling_name = current . name ;
}
catch ( hr_polygon_error & poly ) {
set_geometry ( g ) ;
set_variation ( v ) ;
current = t ;
2020-06-02 00:26:28 +00:00
start_poly_debugger ( poly ) ;
string help = poly . generate_error ( ) ;
2020-05-01 15:12:13 +00:00
showstartmenu = false ;
for ( auto & p : poly . params )
help + = lalign ( - 1 , p . first , " = " , p . second , " \n " ) ;
gotoHelp ( help ) ;
2020-04-26 09:02:03 +00:00
}
catch ( hr_parse_exception & ex ) {
println ( hlog , " failed: " , ex . s ) ;
set_geometry ( g ) ;
current = t ;
start_game ( ) ;
addMessage ( " failed: " + ex . s ) ;
}
2020-05-01 16:11:48 +00:00
catch ( connection_debug_request & cr ) {
set_geometry ( g ) ;
debugged = current ;
current = t ;
ensure_geometry ( cr . c ) ;
debug_polys . clear ( ) ;
debug_polys . emplace_back ( Id , cr . id ) ;
pushScreen ( connection_debugger ) ;
}
2020-04-26 09:02:03 +00:00
start_game ( ) ;
}
2020-06-02 00:29:31 +00:00
string slider_error ;
EX void sliders_changed ( ) {
try {
load ( current . filename , true ) ;
using_slided = true ;
slider_error = " OK " ;
2020-06-06 16:42:58 +00:00
# if CAP_TEXTURE
2020-06-02 00:29:31 +00:00
texture : : config . remap ( ) ;
2020-06-06 16:42:58 +00:00
# endif
2020-06-02 00:29:31 +00:00
}
catch ( hr_parse_exception & ex ) {
using_slided = false ;
slider_error = ex . s ;
}
catch ( hr_polygon_error & poly ) {
using_slided = false ;
slider_error = poly . generate_error ( ) ;
}
}
EX void set_sliders ( ) {
cmode = sm : : SIDE | sm : : MAYDARK ;
gamescreen ( 1 ) ;
dialog : : init ( XLAT ( " tessellation sliders " ) ) ;
char ch = ' A ' ;
for ( auto & sl : current . sliders ) {
dialog : : addSelItem ( sl . name , fts ( sl . current ) , ch + + ) ;
dialog : : add_action ( [ & ] {
dialog : : editNumber ( sl . current , sl . min , sl . max , 1 , sl . zero , sl . name , sl . name ) ;
dialog : : reaction = sliders_changed ;
} ) ;
}
dialog : : addInfo ( slider_error ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
2019-12-14 10:29:53 +00:00
# if CAP_COMMANDLINE
int readArgs ( ) {
using namespace arg ;
if ( 0 ) ;
2020-06-06 16:43:09 +00:00
else if ( argis ( " -tes " ) | | argis ( " -arbi " ) ) {
2019-12-14 10:29:53 +00:00
PHASEFROM ( 2 ) ;
shift ( ) ;
2020-04-26 09:02:03 +00:00
run ( args ( ) ) ;
2019-12-14 10:29:53 +00:00
}
2020-06-06 16:41:36 +00:00
else if ( argis ( " -arb-legacy " ) ) {
legacy = true ;
}
2020-06-03 09:44:00 +00:00
else if ( argis ( " -arb-slider " ) ) {
PHASEFROM ( 2 ) ;
shift ( ) ;
string slider = args ( ) ;
bool found = true ;
for ( auto & sl : current . sliders )
if ( sl . name = = slider ) {
shift_arg_formula ( sl . current , sliders_changed ) ;
found = true ;
}
if ( ! found ) {
println ( hlog , " warning: no slider named " , slider , " found " ) ;
shift ( ) ;
}
}
2019-12-14 10:29:53 +00:00
else return 1 ;
return 0 ;
}
auto hook = addHook ( hooks_args , 100 , readArgs ) ;
# endif
EX bool in ( ) { return geometry = = gArbitrary ; }
2020-04-26 09:02:03 +00:00
EX string tes = " tessellations/sample/marjorie-rice.tes " ;
2019-12-27 01:08:04 +00:00
2019-12-27 21:59:50 +00:00
EX bool linespattern ( cell * c ) {
return current . shapes [ id_of ( c - > master ) ] . flags & arcm : : sfLINE ;
}
EX bool pseudohept ( cell * c ) {
return current . shapes [ id_of ( c - > master ) ] . flags & arcm : : sfPH ;
}
2019-12-27 01:08:04 +00:00
EX void choose ( ) {
dialog : : openFileDialog ( tes , XLAT ( " open a tiling " ) , " .tes " ,
[ ] ( ) {
2020-04-26 09:02:03 +00:00
run ( tes ) ;
2019-12-27 01:08:04 +00:00
return true ;
} ) ;
}
2020-06-03 14:42:26 +00:00
# if MAXMDIM >= 4
auto hooksw = addHook ( hooks_swapdim , 100 , [ ] {
for ( auto & p : { & current , & slided } )
for ( auto & s : p - > shapes )
for ( auto & v : s . vertices )
swapmatrix ( v ) ;
for ( auto & p : altmap ) for ( auto & pp : p . second ) swapmatrix ( pp . second ) ;
for ( auto & p : arbi_matrix ) swapmatrix ( p . second . second ) ;
} ) ;
# endif
2019-12-14 10:29:53 +00:00
EX }
2020-05-03 19:11:49 +00:00
}