diff --git a/simulation/simulationstateserializer.cpp b/simulation/simulationstateserializer.cpp index ec801024..5a393ee1 100644 --- a/simulation/simulationstateserializer.cpp +++ b/simulation/simulationstateserializer.cpp @@ -48,6 +48,13 @@ double vehicle_length( std::string const &Folder, std::string const &Type ) { return probe.LoadFIZ( paths::dynamic + Folder + "/" ) ? probe.Dim.L : 0.0; } +// the catenary straight in the scenery file belongs to the file (layer) it's in, the scenery save writes it there +std::string catenary_file( cParser const &Input ) { + + auto const layer { scene::Layers.handle() }; + return ( ( layer != null_handle && Input.InLayerFile() ) ? scene::Layers.layer( layer ).name : Input.Name() ); +} + } // namespace std::shared_ptr @@ -177,6 +184,10 @@ state_serializer::deserialize_continue(std::shared_ptr state // sieć trakcyjna: plik z podwieszeniami, "catenary endcatenary" catenary_scene_load( Input ); } + else if( token == "skin" ) { + // domyślna tekstura podwieszeń sieci z tego pliku: plik sieci z dyrektywy "catenary" dołączony jako warstwa + catenary_scene_skin( Input, catenary_file( Input ) ); + } else { ErrorLog( "Bad scenario: unexpected token \"" + token + "\" defined in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() - 1 ) + ")" ); } @@ -457,6 +468,8 @@ state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Sc simulation::Roads.InitRoads(); // crossings and traffic points go by the lanes, which are complete at this point simulation::Roadpoints.InitRoadpoints(); + // the catenary entries of the scenery files make their spans before these get tied together + catenary_scene_loaded(); simulation::Traction.InitTraction(); catenary_scene_resolve(); simulation::Events.InitEvents(); @@ -516,6 +529,8 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch } // a run of plain model instances is taken straight from the text, instead of token by token if( true == deserialize_models( Input, Scratchpad, nodedata, inputline, sourcebegin ) ) { return; } + // the text of the header as written, for the entries which need the case of their names + auto const header { Input.remainingText() }; // common data and node type indicator Input.getTokens( 4 ); Input @@ -584,10 +599,27 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch scene::Layers.track( road, { sourcebegin, Input.TokenEnd() } ); } else if( nodedata.type == "suspension" || nodedata.type == "chain" || nodedata.type == "gantry" || nodedata.type == "anchor" ) { - // the catenary straight in the scenery file: it belongs to the file (layer) it's in, the scenery save writes it there - auto const layer { scene::Layers.handle() }; - auto const file { ( layer != null_handle && Input.InLayerFile() ) ? scene::Layers.layer( layer ).name : Input.Name() }; - catenary_scene_node( Input, nodedata.type, nodedata.range_max, nodedata.range_min, nodedata.name, file ); + // the names are referred to by the other entries as they're written, the parser gives them in lower case + auto name { nodedata.name }; + if( false == header.empty() ) { + std::string_view words[ 4 ]; + auto const breaks { std::string_view { "\n\r\t ;" } }; + std::size_t position { 0 }; + for( auto &word : words ) { + position = std::min( header.find_first_not_of( breaks, position ), header.size() ); + auto const end { std::min( header.find_first_of( breaks, position ), header.size() ) }; + word = header.substr( position, end - position ); + position = end; + } + auto lower { std::string { words[ 2 ] } }; + std::transform( lower.begin(), lower.end(), lower.begin(), []( unsigned char const C ) { return static_cast( std::tolower( C ) ); } ); + auto type { std::string { words[ 3 ] } }; + std::transform( type.begin(), type.end(), type.begin(), []( unsigned char const C ) { return static_cast( std::tolower( C ) ); } ); + if( false == name.empty() && lower == name && type == nodedata.type ) { + name = words[ 2 ]; + } + } + catenary_scene_node( Input, nodedata.type, nodedata.range_max, nodedata.range_min, name, catenary_file( Input ) ); } else if( nodedata.type == "sweep" ) { diff --git a/world/catenary_scene.cpp b/world/catenary_scene.cpp index e24098dd..d965070f 100644 --- a/world/catenary_scene.cpp +++ b/world/catenary_scene.cpp @@ -30,6 +30,7 @@ http://mozilla.org/MPL/2.0/. #include #include +#include #include #include #include @@ -177,6 +178,9 @@ file_named( std::string const &Name ) { std::size_t Resolved { 0 }; // czy sceneria przeszła już inicjalizację (FirstInit); plik podwieszeń dołączony później jest wiązany od razu bool Initialized { false }; +// wpisy node sieci z plików scenerii przed FirstInit, po plikach w kolejności wczytania: dodawane razem tuż przed InitTraction, +// jak plik z dyrektywy - wtedy kolejność wpisów w pliku nie ma znaczenia (łańcuch przed swoimi bramkami i kotwieniami) +std::vector> Pending; // uwagi solvera już wypisane do logu; ta sama uwaga dla tej samej karty idzie tylko raz, żeby nie zalać logu std::set Notes; @@ -1657,7 +1661,15 @@ namespace { // bramki, kotwienia i łańcuchy są od razu void add_content( catenary::suspension_file const &File, std::string const &Filename ) { + // ten sam wpis drugi raz (plik dołączony dwa razy, dyrektywą i jako warstwa): pomijany. Inaczej w scenie są dwie kopie + // o tej samej nazwie, edytor zmienia jedną, a druga zostaje na starym miejscu + auto const twice = [ & ]( char const *Kind, std::string const &Name, bool const Taken ) { + if( Name.empty() || false == Taken ) { return false; } + ErrorLog( "Bad scenario: " + std::string( Kind ) + " \"" + Name + "\" from \"" + Filename + "\" is defined more than once (the file is included twice?); the copy is skipped" ); + return true; + }; for( auto const &entry : File.entries ) { + if( twice( "suspension", entry.name, suspension_named( Filename, entry.name ) < Suspensions.size() ) ) { continue; } scene_suspension suspension; suspension.data = entry; suspension.skin = ( entry.skin.empty() ? File.skin : entry.skin ); @@ -1665,6 +1677,7 @@ add_content( catenary::suspension_file const &File, std::string const &Filename Suspensions.emplace_back( suspension ); } for( auto const &line : File.gantries ) { + if( twice( "gantry", line.name, std::any_of( Gantries.begin(), Gantries.end(), [ & ]( scene_gantry const &Gantry ) { return false == Gantry.removed && Gantry.file == Filename && Gantry.data.name == line.name; } ) ) ) { continue; } scene_gantry gantry; gantry.data = line; gantry.file = Filename; @@ -1673,6 +1686,7 @@ add_content( catenary::suspension_file const &File, std::string const &Filename } // kotwienia przed łańcuchami: ostatnie przęsło łańcucha kończy się na ich urządzeniu (łańcuch wpisany wcześniej idzie za nim) for( auto const &line : File.anchors ) { + if( twice( "anchor", line.name, anchor_named( Filename, line.name ) != nullptr ) ) { continue; } scene_anchor anchor; anchor.data = line; anchor.file = Filename; @@ -1682,6 +1696,7 @@ add_content( catenary::suspension_file const &File, std::string const &Filename } // przęsła łańcuchów powstają od razu: przed inicjalizacją scenerii łączy je InitTraction razem z resztą sieci for( auto const &line : File.chains ) { + if( twice( "chain", line.name, std::any_of( Chains.begin(), Chains.end(), [ & ]( scene_chain const &Chain ) { return false == Chain.removed && Chain.file == Filename && Chain.data.name == line.name; } ) ) ) { continue; } scene_chain chain; chain.data = line; chain.file = Filename; @@ -1724,10 +1739,56 @@ catenary_scene_node( cParser &Input, std::string const &Type, double const Range Files.emplace_back( known ); } FileLayers.emplace( File, static_cast( scene::Layers.handle() ) ); - add_content( file, File ); if( Initialized ) { // wpis po FirstInit: tory, przęsła i modele już są + add_content( file, File ); catenary_scene_resolve(); + return; + } + auto pending { std::find_if( Pending.begin(), Pending.end(), [ & ]( auto const &Entry ) { return Entry.first == File; } ) }; + if( pending == Pending.end() ) { + Pending.emplace_back( File, catenary::suspension_file() ); + pending = std::prev( Pending.end() ); + } + auto &target { pending->second }; + target.entries.insert( target.entries.end(), file.entries.begin(), file.entries.end() ); + target.chains.insert( target.chains.end(), file.chains.begin(), file.chains.end() ); + target.gantries.insert( target.gantries.end(), file.gantries.begin(), file.gantries.end() ); + target.anchors.insert( target.anchors.end(), file.anchors.begin(), file.anchors.end() ); +} + +void +catenary_scene_skin( cParser &Input, std::string const &File ) { + + auto const skin { Input.getToken( false ) }; + if( skin.empty() ) { + ErrorLog( "Bad scenario: \"skin\" without a texture name in file \"" + Input.Name() + "\" (line " + std::to_string( Input.Line() ) + ")" ); + return; + } + if( false == Global.bLoadTraction ) { return; } + InlineFiles.emplace( File ); + if( file_named( File ) == nullptr ) { + scene_file known; + known.name = File; + Files.emplace_back( known ); + } + file_named( File )->skin = skin; + if( Initialized ) { return; } + auto pending { std::find_if( Pending.begin(), Pending.end(), [ & ]( auto const &Entry ) { return Entry.first == File; } ) }; + if( pending == Pending.end() ) { + Pending.emplace_back( File, catenary::suspension_file() ); + pending = std::prev( Pending.end() ); + } + pending->second.skin = skin; +} + +void +catenary_scene_loaded() { + + auto pending { std::move( Pending ) }; + Pending.clear(); + for( auto const &file : pending ) { + add_content( file.second, file.first ); } } @@ -1798,6 +1859,8 @@ catenary_scene_load( cParser &Input ) { void catenary_scene_resolve() { + // wpisy ze scenerii bez wywołania catenary_scene_loaded() przed InitTraction + if( false == Initialized && false == Pending.empty() ) { catenary_scene_loaded(); } Initialized = true; if( Resolved >= Suspensions.size() ) { return; } @@ -1891,6 +1954,7 @@ catenary_scene_clear() { Files.clear(); FileLayers.clear(); InlineFiles.clear(); + Pending.clear(); Notes.clear(); Resolved = 0; Initialized = false; diff --git a/world/catenary_scene.h b/world/catenary_scene.h index 795c4e08..de60cc4a 100644 --- a/world/catenary_scene.h +++ b/world/catenary_scene.h @@ -21,6 +21,10 @@ void catenary_scene_load( cParser &Input ); // wpis "node suspension|chain|gantry|anchor ... end" wprost w pliku scenerii; Input stoi za typem. // File: plik scenerii (warstwa), do którego wpis należy - jego wpisy sieci zapisuje zapis scenerii void catenary_scene_node( cParser &Input, std::string const &Type, double RangeMax, double RangeMin, std::string const &Name, std::string const &File ); +// "skin " w pliku scenerii: domyślna tekstura podwieszeń tego pliku (jak w osobnym pliku sieci) +void catenary_scene_skin( cParser &Input, std::string const &File ); +// po wczytaniu wpisów scenerii, przed InitTraction: dodaje zebrane wpisy sieci z plików scenerii +void catenary_scene_loaded(); // po wczytaniu scenerii: wiąże podwieszenia z konstrukcjami wsporczymi i przęsłami po nazwach, zgłasza braki void catenary_scene_resolve(); // przed wczytaniem nowej scenerii: usuwa podwieszenia poprzedniej