17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-02 17:29:14 +01:00

Catenary as plain node entries in scenery files

This commit is contained in:
2026-10-26 20:23:26 +01:00
parent d794b1e736
commit e19c788f30
9 changed files with 356 additions and 71 deletions

View File

@@ -3004,15 +3004,30 @@ bool editor_mode::save()
std::transform(b.begin(), b.end(), b.begin(), [](unsigned char const C) { return static_cast<char>(std::tolower(C)); }); std::transform(b.begin(), b.end(), b.begin(), [](unsigned char const C) { return static_cast<char>(std::tolower(C)); });
return a == b; return a == b;
}; };
// the catenary straight in the scenery files: the node entries of the changed ones go into the files of their layers anew
scene::node_blocks catenaryblocks;
catenaryblocks.kinds = {"suspension", "chain", "gantry", "anchor"};
std::vector<std::string> catenaryinline;
for (auto const &file : catenary_scene_files()) for (auto const &file : catenary_scene_files())
{ {
// a catenary file of the name of a scenery file would take its place, and the scenery its directive if (false == catenary_scene_file_inline(file) || false == catenary_scene_file_changed(file))
for (std::size_t layer = 1; layer <= scene::Layers.size(); ++layer) continue;
scene::layer_handle target{null_handle};
for (std::size_t layer = 1; layer <= scene::Layers.size() && target == null_handle; ++layer)
if (scene::Layers.listed(static_cast<scene::layer_handle>(layer)) && same_file(scene::Layers.layer(static_cast<scene::layer_handle>(layer)).name, file)) if (scene::Layers.listed(static_cast<scene::layer_handle>(layer)) && same_file(scene::Layers.layer(static_cast<scene::layer_handle>(layer)).name, file))
{ target = static_cast<scene::layer_handle>(layer);
ui()->set_status(format(STR_C("The catenary couldn't be saved: its file \"%s\" is a file of the scenery; give the catenary another file"), file.c_str()), true); std::string reason;
return false; if (target == null_handle)
} reason = STR("it isn't in a scenery file the editor can rewrite (an include template)");
else
scene::Layers.accepts(target, &reason);
if (target == null_handle || false == scene::Layers.accepts(target))
{
ui()->set_status(format(STR_C("The catenary in \"%s\" couldn't be saved: %s"), file.c_str(), reason.c_str()), true);
return false;
}
catenaryblocks.lines[target] = catenary_scene_entries(file);
catenaryinline.push_back(file);
} }
if (catenary_scene_changed()) if (catenary_scene_changed())
{ {
@@ -3055,7 +3070,10 @@ bool editor_mode::save()
trailing.insert(trailing.end(), trainset.lines.begin(), trainset.lines.end()); trailing.insert(trailing.end(), trainset.lines.begin(), trainset.lines.end());
if (placed == 0) if (placed == 0)
starter_trainset(trailing); starter_trainset(trailing);
auto const result = scene::Layers.save(rootstatements, trailing, layerstatements); auto const result = scene::Layers.save(rootstatements, trailing, layerstatements, &catenaryblocks);
if (result.success)
for (auto const &file : catenaryinline)
catenary_scene_file_saved(file);
if (result.success && controls) if (result.success && controls)
m_turntable_included = true; m_turntable_included = true;
if (result.success && noses) if (result.success && noses)

View File

@@ -695,29 +695,34 @@ void catenary_editor::catenary_file_follow()
auto const active{scene::Layers.active()}; auto const active{scene::Layers.active()};
if (false == scene::Layers.valid(active)) if (false == scene::Layers.valid(active))
return; // the scenery wasn't opened for editing: no layers return; // the scenery wasn't opened for editing: no layers
// the catenary file of the active layer (the one it includes), or a new one named after it next to it; not while a chain // the new poles go into the file of the active layer, as its node entries; not while a chain goes on (a chain is in one
// goes on (a chain is in one file) nor when the user gave the file // file) nor when the user gave the file
if (false == tool.file_own && tool.last < 0 && tool.selected < 0) if (false == tool.file_own && tool.last < 0 && tool.selected < 0)
{ std::snprintf(tool.file, sizeof(tool.file), "%s", scene::Layers.layer(active).name.c_str());
std::string wanted; if (tool.file[0] == '\0')
for (auto const &file : catenary_scene_files()) return;
if (catenary_scene_file_layer(file) == active) // the file of a layer takes the entries straight; another one is a catenary file of its own (catenary <file> endcatenary),
{ // whose directive goes into the file of the active layer
wanted = file; if (catenary_layer_of(tool.file) != 0)
break; catenary_scene_file_inline(tool.file, true);
} else if (false == catenary_scene_file_inline(tool.file))
if (wanted.empty())
{
std::filesystem::path const name{scene::Layers.layer(active).name};
wanted = (name.parent_path() / (name.stem().string() + "_siec.scm")).generic_string();
}
std::snprintf(tool.file, sizeof(tool.file), "%s", wanted.c_str());
}
// a file the scenery doesn't include yet gets its directive in the file of the active layer
if (tool.file[0] != '\0')
catenary_scene_file_layer(tool.file, active); catenary_scene_file_layer(tool.file, active);
} }
std::size_t catenary_editor::catenary_layer_of(std::string const &File)
{
auto const same = [](std::string const &A, std::string const &B) {
auto a{std::filesystem::path(A).lexically_normal().generic_string()}, b{std::filesystem::path(B).lexically_normal().generic_string()};
std::transform(a.begin(), a.end(), a.begin(), [](unsigned char const C) { return static_cast<char>(std::tolower(C)); });
std::transform(b.begin(), b.end(), b.begin(), [](unsigned char const C) { return static_cast<char>(std::tolower(C)); });
return a == b;
};
for (std::size_t layer = 1; layer <= scene::Layers.size(); ++layer)
if (scene::Layers.listed(static_cast<scene::layer_handle>(layer)) && same(scene::Layers.layer(static_cast<scene::layer_handle>(layer)).name, File))
return layer;
return 0;
}
bool catenary_editor::typed_span() const bool catenary_editor::typed_span() const
{ {
auto const &tool{m_catenary}; auto const &tool{m_catenary};
@@ -4278,7 +4283,7 @@ void catenary_editor::render_catenary_cantilever_ui()
if (ImGui::InputText(STR_C("Catenary file"), tool.file, sizeof(tool.file))) if (ImGui::InputText(STR_C("Catenary file"), tool.file, sizeof(tool.file)))
tool.file_own = true; tool.file_own = true;
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("File in the scenery directory the new poles go to; a new one is added to the scenery with the directive catenary <file> endcatenary when it's saved, in the file of the active layer. By default the catenary file of the active layer, or a new one named after it")); ImGui::SetTooltip("%s", STR_C("Where the new poles go: by default the file of the active layer, as its node entries (node ... suspension / chain / gantry / anchor), saved with the scenery. Another file name makes a catenary file of its own, included with catenary <file> endcatenary from the file of the active layer"));
if (tool.file_own) if (tool.file_own)
{ {
ImGui::SameLine(); ImGui::SameLine();
@@ -4288,15 +4293,20 @@ void catenary_editor::render_catenary_cantilever_ui()
ImGui::SetTooltip("%s", STR_C("The catenary file follows the active layer again")); ImGui::SetTooltip("%s", STR_C("The catenary file follows the active layer again"));
} }
{ {
// where it ends up: the file, and the layer whose file includes it // where it ends up: straight in the file of a layer, or a catenary file of its own and the layer whose file includes it
auto const layer{static_cast<scene::layer_handle>(catenary_scene_file_layer(tool.file))}; if (catenary_scene_file_inline(tool.file))
auto const files{catenary_scene_files()}; ImGui::TextDisabled("%s", STR_C("node entries in this scenery file (layer), saved with the scenery"));
auto const known{std::find(files.begin(), files.end(), std::string{tool.file}) != files.end()}; else
auto const fresh{catenary_scene_new_files()}; {
auto const created{known && std::find(fresh.begin(), fresh.end(), std::string{tool.file}) != fresh.end()}; auto const layer{static_cast<scene::layer_handle>(catenary_scene_file_layer(tool.file))};
auto const layername{scene::Layers.valid(layer) ? scene::Layers.layer(layer).name : std::string{}}; auto const files{catenary_scene_files()};
if (false == layername.empty()) auto const known{std::find(files.begin(), files.end(), std::string{tool.file}) != files.end()};
ImGui::TextDisabled("%s", format(known && false == created ? STR_C("included by %s") : STR_C("new: the save writes it and its directive into %s"), layername.c_str()).c_str()); auto const fresh{catenary_scene_new_files()};
auto const created{known && std::find(fresh.begin(), fresh.end(), std::string{tool.file}) != fresh.end()};
auto const layername{scene::Layers.valid(layer) ? scene::Layers.layer(layer).name : std::string{}};
if (false == layername.empty())
ImGui::TextDisabled("%s", format(known && false == created ? STR_C("catenary file included by %s") : STR_C("catenary file of its own: the save writes it and its directive into %s"), layername.c_str()).c_str());
}
} }
render_catenary_chain_state(); render_catenary_chain_state();
} }

View File

@@ -170,6 +170,8 @@ struct catenary_editor
void catenary_scan(); void catenary_scan();
// the catenary file of the new poles follows the active layer of the scenery // the catenary file of the new poles follows the active layer of the scenery
void catenary_file_follow(); void catenary_file_follow();
// the scenery layer of this file name; 0 none
static std::size_t catenary_layer_of(std::string const &File);
// Placing: the spot of a new pole, which takes the zigzag proposed after the last pole and the step of the span // Placing: the spot of a new pole, which takes the zigzag proposed after the last pole and the step of the span
bool catenary_spot_at(TTrack &Track, int const Path, glm::dvec3 const &Point, glm::dvec3 const &Cursor, int const Side, catenary_spot &Spot, bool const Placing = false) const; bool catenary_spot_at(TTrack &Track, int const Path, glm::dvec3 const &Point, glm::dvec3 const &Cursor, int const Side, catenary_spot &Spot, bool const Placing = false) const;
// the zigzag (Iet-107): the largest staggers allowed for the speed of the line, and those the next poles get // the zigzag (Iet-107): the largest staggers allowed for the speed of the line, and those the next poles get

View File

@@ -11747,3 +11747,23 @@ msgstr "Część sieci trakcyjnej: jej kotwienie jest zaznaczone w narzędziu Si
msgid "A part of the catenary: its chain is selected in the Catenary tool" msgid "A part of the catenary: its chain is selected in the Catenary tool"
msgstr "Część sieci trakcyjnej: jej łańcuch jest zaznaczony w narzędziu Sieć trakcyjna" msgstr "Część sieci trakcyjnej: jej łańcuch jest zaznaczony w narzędziu Sieć trakcyjna"
# scenery editor: catenary - straight in the scenery files
msgid "node entries in this scenery file (layer), saved with the scenery"
msgstr "wpisy node w tym pliku scenerii (warstwie), zapisywane razem ze scenerią"
msgid "catenary file included by %s"
msgstr "osobny plik sieci, dołączany przez %s"
msgid "catenary file of its own: the save writes it and its directive into %s"
msgstr "osobny plik sieci: zapis utworzy go i dopisze dyrektywę do %s"
msgid "Where the new poles go: by default the file of the active layer, as its node entries (node ... suspension / chain / gantry / anchor), saved with the scenery. Another file name makes a catenary file of its own, included with catenary <file> endcatenary from the file of the active layer"
msgstr "Gdzie trafiają nowe słupy: domyślnie do pliku aktywnej warstwy, jako jej wpisy node (node ... suspension / chain / gantry / anchor), zapisywane razem ze scenerią. Inna nazwa pliku tworzy osobny plik sieci, dołączany dyrektywą catenary <plik> endcatenary z pliku aktywnej warstwy"
msgid "it isn't in a scenery file the editor can rewrite (an include template)"
msgstr "nie jest w pliku scenerii, który edytor może przepisać (szablon include)"
msgid "The catenary in \"%s\" couldn't be saved: %s"
msgstr "Nie udało się zapisać sieci trakcyjnej w \"%s\": %s"

View File

@@ -144,6 +144,14 @@ struct shape_source
bool erased{false}; // the definition goes from the file on save bool erased{false}; // the definition goes from the file on save
}; };
// node definitions the editor writes all at once for a layer (the catenary): on save the definitions "node ... <kind> ... end<kind>"
// of these kinds are taken out of the files of the given layers and the given lines go into them instead, like the new definitions
struct node_blocks
{
std::set<std::string> kinds; // node types, lower case
std::map<layer_handle, std::vector<std::string>> lines;
};
struct save_result struct save_result
{ {
bool success{false}; bool success{false};
@@ -350,7 +358,8 @@ class node_layers
// memory cells, and layer changes. the files are left intact unless the whole operation can be performed. // memory cells, and layer changes. the files are left intact unless the whole operation can be performed.
// Rootstatements: additional statements to place at the end of the scenario file // Rootstatements: additional statements to place at the end of the scenario file
// Layerstatements: additional statements to place in the files of specified layers, the same way // Layerstatements: additional statements to place in the files of specified layers, the same way
save_result save(std::vector<std::string> const &Rootstatements = {}, std::vector<std::string> const &Trailingstatements = {}, std::map<layer_handle, std::vector<std::string>> const &Layerstatements = {}); // Blocks: node definitions written anew as a whole, see node_blocks
save_result save(std::vector<std::string> const &Rootstatements = {}, std::vector<std::string> const &Trailingstatements = {}, std::map<layer_handle, std::vector<std::string>> const &Layerstatements = {}, node_blocks const *Blocks = nullptr);
std::string check_sources() const; std::string check_sources() const;
private: private:

View File

@@ -633,7 +633,75 @@ bool node_layers::compose(save_state &State, layer_handle const Layer, compositi
} }
// writes changes made in the editor to the scenery files // writes changes made in the editor to the scenery files
save_result node_layers::save(std::vector<std::string> const &Rootstatements, std::vector<std::string> const &Trailingstatements, std::map<layer_handle, std::vector<std::string>> const &Layerstatements) namespace
{
// locations of the node definitions of specified kinds in the text of a scenery file: from "node" to the closing "end<kind>",
// tokens split as the parser does it, comments skipped
std::vector<source_span> node_definitions(std::string const &Text, std::set<std::string> const &Kinds)
{
std::vector<source_span> result;
std::size_t position{0};
auto const size{Text.size()};
auto const separator = [&](std::size_t const At) { return Text[At] == ' ' || Text[At] == '\t' || Text[At] == '\n' || Text[At] == '\r' || Text[At] == ';'; };
auto const comment = [&](std::size_t const At) { return At + 1 < size && Text[At] == '/' && (Text[At + 1] == '/' || Text[At + 1] == '*'); };
// the next token: its range, false at the end of the text
auto const next = [&](std::size_t &Begin, std::size_t &End) {
while (position < size)
{
if (separator(position))
++position;
else if (comment(position))
{
auto const block{Text[position + 1] == '*'};
auto const close{block ? Text.find("*/", position + 2) : Text.find('\n', position + 2)};
position = close == std::string::npos ? size : close + (block ? 2 : 1);
}
else
break;
}
if (position >= size)
return false;
Begin = position;
while (position < size && false == separator(position) && false == comment(position))
++position;
End = position;
return true;
};
auto const word = [&](std::size_t const Begin, std::size_t const End) {
auto text{Text.substr(Begin, End - Begin)};
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char const C) { return static_cast<char>(std::tolower(C)); });
return text;
};
std::size_t begin{0}, end{0};
while (next(begin, end))
{
if (word(begin, end) != "node")
continue;
auto const start{begin};
// range max, range min, name, type
auto complete{true};
for (int k = 0; k < 4 && complete; ++k)
complete = next(begin, end);
if (false == complete)
break;
auto const kind{word(begin, end)};
if (Kinds.count(kind) == 0)
continue;
auto const closing{"end" + kind};
while (next(begin, end))
if (word(begin, end) == closing)
{
result.push_back({static_cast<std::streamoff>(start), static_cast<std::streamoff>(end)});
break;
}
}
return result;
}
} // namespace
save_result node_layers::save(std::vector<std::string> const &Rootstatements, std::vector<std::string> const &Trailingstatements, std::map<layer_handle, std::vector<std::string>> const &Layerstatements, node_blocks const *Blocks)
{ {
save_result result; save_result result;
if (m_layers.empty()) if (m_layers.empty())
@@ -1316,6 +1384,30 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements, st
} }
} }
// node definitions written anew as a whole: the old ones go, the new ones are added to the file like new definitions
std::map<layer_handle, std::vector<std::string>> blocktext;
if (Blocks != nullptr)
{
for (auto const &[blocklayer, lines] : Blocks->lines)
{
auto const target{resolve(blocklayer)};
if (false == is_output(target))
{
continue;
}
if (false == load(target))
{
return fail(state.error);
}
for (auto const &span : node_definitions(state.content[target], Blocks->kinds))
{
push_edit(target, span, {}, nullptr);
}
auto &text{blocktext[target]};
text.insert(text.end(), lines.begin(), lines.end());
}
}
// includes of templates // includes of templates
std::map<layer_handle, std::vector<instance_handle>> placed; // directives made in the editor, by target layer std::map<layer_handle, std::vector<instance_handle>> placed; // directives made in the editor, by target layer
for (std::size_t idx = 0; idx < m_instances.size(); ++idx) for (std::size_t idx = 0; idx < m_instances.size(); ++idx)
@@ -1455,7 +1547,8 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements, st
continue; continue;
} }
if (layer(candidate).created || state.patches.count(candidate) != 0 || appended.count(candidate) != 0 || created.count(candidate) != 0 || placed.count(candidate) != 0 || if (layer(candidate).created || state.patches.count(candidate) != 0 || appended.count(candidate) != 0 || created.count(candidate) != 0 || placed.count(candidate) != 0 ||
markedtext.count(candidate) != 0 || (candidate == root && (false == Rootstatements.empty() || false == Trailingstatements.empty())) || Layerstatements.count(candidate) != 0) markedtext.count(candidate) != 0 || (candidate == root && (false == Rootstatements.empty() || false == Trailingstatements.empty())) || Layerstatements.count(candidate) != 0 ||
blocktext.count(candidate) != 0)
{ {
outputs.emplace_back(candidate); outputs.emplace_back(candidate);
} }
@@ -1576,6 +1669,14 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements, st
added.text += statement + eol; added.text += statement + eol;
} }
} }
if (auto const lookup{blocktext.find(output)}; lookup != blocktext.end())
{
for (auto const &line : lookup->second)
{
ensure_newline(added.text, eol);
added.text += line + eol;
}
}
if (output == root && false == Trailingstatements.empty()) if (output == root && false == Trailingstatements.empty())
{ {
composition trailing; composition trailing;

View File

@@ -583,6 +583,12 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
road->create_lanes(); road->create_lanes();
scene::Layers.track( road, { sourcebegin, Input.TokenEnd() } ); 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 );
}
else if( nodedata.type == "sweep" ) { else if( nodedata.type == "sweep" ) {
auto *sweep { new sweep_node( nodedata ) }; auto *sweep { new sweep_node( nodedata ) };

View File

@@ -78,6 +78,8 @@ struct scene_file {
std::vector<scene_file> Files; std::vector<scene_file> Files;
// warstwa scenerii (plik), która dołącza plik sieci dyrektywą "catenary"; dla plików z edytora ta, do której trafi dyrektywa // warstwa scenerii (plik), która dołącza plik sieci dyrektywą "catenary"; dla plików z edytora ta, do której trafi dyrektywa
std::map<std::string, std::size_t> FileLayers; std::map<std::string, std::size_t> FileLayers;
// "pliki" sieci, których wpisy są zwykłymi wpisami node w pliku scenerii (warstwie): zapisuje je zapis scenerii, nie catenary_scene_save()
std::set<std::string> InlineFiles;
// łańcuch sieci z pliku: przęsła od podwieszenia do podwieszenia // łańcuch sieci z pliku: przęsła od podwieszenia do podwieszenia
struct scene_chain { struct scene_chain {
@@ -1649,6 +1651,86 @@ umbrella_members( std::string const &Name ) {
} // namespace } // namespace
namespace {
// wpisy rozebranego pliku (albo jednego wpisu node ze scenerii) do sceny: podwieszenia czekają na catenary_scene_resolve(),
// bramki, kotwienia i łańcuchy są od razu
void
add_content( catenary::suspension_file const &File, std::string const &Filename ) {
for( auto const &entry : File.entries ) {
scene_suspension suspension;
suspension.data = entry;
suspension.skin = ( entry.skin.empty() ? File.skin : entry.skin );
suspension.file = Filename;
Suspensions.emplace_back( suspension );
}
for( auto const &line : File.gantries ) {
scene_gantry gantry;
gantry.data = line;
gantry.file = Filename;
Gantries.emplace_back( gantry );
assemble_gantry( Gantries.back() );
}
// 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 ) {
scene_anchor anchor;
anchor.data = line;
anchor.file = Filename;
Anchors.emplace_back( anchor );
assemble_anchor( Anchors.back() );
follow_anchor( Anchors.back() );
}
// 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 ) {
scene_chain chain;
chain.data = line;
chain.file = Filename;
Chains.emplace_back( chain );
rebuild_chain( Chains.back() );
}
}
} // namespace
void
catenary_scene_node( cParser &Input, std::string const &Type, double const RangeMax, double const RangeMin, std::string const &Name, std::string const &File ) {
// treść wpisu do jego końca, z wielkością liter (symbole typów sieci, ścieżki tekstur)
auto const closing { "end" + Type };
std::vector<std::string> tokens { "node", std::to_string( RangeMax ), std::to_string( RangeMin ), ( Name.empty() ? std::string( "none" ) : Name ), Type };
auto const line { Input.Line() };
while( true ) {
auto token { Input.getToken<std::string>( false ) };
if( token.empty() ) {
ErrorLog( "Bad scenario: " + Type + " \"" + Name + "\" without \"" + closing + "\" in file \"" + Input.Name() + "\" (line " + std::to_string( line ) + ")" );
return;
}
auto lower { token };
std::transform( lower.begin(), lower.end(), lower.begin(), []( unsigned char const C ) { return static_cast<char>( std::tolower( C ) ); } );
tokens.emplace_back( token );
if( lower == closing ) { tokens.back() = closing; break; }
}
if( false == Global.bLoadTraction ) { return; }
catenary::suspension_file file;
std::vector<std::string> errors;
catenary::parse_suspensions( tokens, File, file, errors );
for( auto const &error : errors ) {
ErrorLog( error );
}
InlineFiles.emplace( File );
if( file_named( File ) == nullptr ) {
scene_file known;
known.name = File;
Files.emplace_back( known );
}
FileLayers.emplace( File, static_cast<std::size_t>( scene::Layers.handle() ) );
add_content( file, File );
if( Initialized ) {
// wpis po FirstInit: tory, przęsła i modele już są
catenary_scene_resolve();
}
}
// obsługa dyrektywy "catenary" ze scenerii: wczytuje wskazany plik podwieszeń // obsługa dyrektywy "catenary" ze scenerii: wczytuje wskazany plik podwieszeń
void void
catenary_scene_load( cParser &Input ) { catenary_scene_load( cParser &Input ) {
@@ -1686,36 +1768,7 @@ catenary_scene_load( cParser &Input ) {
for( auto const &error : errors ) { for( auto const &error : errors ) {
ErrorLog( error ); ErrorLog( error );
} }
for( auto const &entry : file.entries ) { add_content( file, filename );
scene_suspension suspension;
suspension.data = entry;
suspension.skin = ( entry.skin.empty() ? file.skin : entry.skin );
suspension.file = filename;
Suspensions.emplace_back( suspension );
}
for( auto const &line : file.gantries ) {
scene_gantry gantry;
gantry.data = line;
gantry.file = filename;
Gantries.emplace_back( gantry );
assemble_gantry( Gantries.back() );
}
// kotwienia przed łańcuchami: ostatnie przęsło łańcucha kończy się na ich urządzeniu
for( auto const &line : file.anchors ) {
scene_anchor anchor;
anchor.data = line;
anchor.file = filename;
Anchors.emplace_back( anchor );
assemble_anchor( Anchors.back() );
}
// 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 ) {
scene_chain chain;
chain.data = line;
chain.file = filename;
Chains.emplace_back( chain );
rebuild_chain( Chains.back() );
}
// wczytywany z pliku tej warstwy (tylko przy otwarciu scenerii do edycji, inaczej warstw nie ma) // wczytywany z pliku tej warstwy (tylko przy otwarciu scenerii do edycji, inaczej warstw nie ma)
FileLayers.emplace( filename, static_cast<std::size_t>( scene::Layers.handle() ) ); FileLayers.emplace( filename, static_cast<std::size_t>( scene::Layers.handle() ) );
if( file_named( filename ) == nullptr ) { if( file_named( filename ) == nullptr ) {
@@ -1837,6 +1890,7 @@ catenary_scene_clear() {
Anchors.clear(); Anchors.clear();
Files.clear(); Files.clear();
FileLayers.clear(); FileLayers.clear();
InlineFiles.clear();
Notes.clear(); Notes.clear();
Resolved = 0; Resolved = 0;
Initialized = false; Initialized = false;
@@ -2090,10 +2144,59 @@ catenary_scene_changed() {
std::vector<std::string> std::vector<std::string>
catenary_scene_new_files() { catenary_scene_new_files() {
std::vector<std::string> names; std::vector<std::string> names;
for( auto const &file : Files ) { if( file.created ) { names.push_back( file.name ); } } for( auto const &file : Files ) { if( file.created && InlineFiles.count( file.name ) == 0 ) { names.push_back( file.name ); } }
return names; return names;
} }
bool
catenary_scene_file_inline( std::string const &File ) {
return InlineFiles.count( File ) != 0;
}
void
catenary_scene_file_inline( std::string const &File, bool const Inline ) {
if( Inline ) { InlineFiles.emplace( File ); }
else { InlineFiles.erase( File ); }
}
bool
catenary_scene_file_changed( std::string const &File ) {
auto const *file { file_named( File ) };
return file != nullptr && file->changed;
}
void
catenary_scene_file_saved( std::string const &File ) {
if( auto *file { file_named( File ) }; file != nullptr ) {
file->changed = false;
file->created = false;
}
}
std::vector<std::string>
catenary_scene_entries( std::string const &File ) {
// w kolejności wczytywania wpisów node: podwieszenia, bramki, kotwienia, łańcuchy
std::vector<std::string> lines;
auto const *file { file_named( File ) };
auto const skin { file != nullptr ? file->skin : std::string() };
for( auto const &suspension : Suspensions ) {
if( suspension.removed || suspension.file != File ) { continue; }
auto data { suspension.data };
if( data.skin.empty() ) { data.skin = skin; }
lines.emplace_back( catenary::write_suspension( data ) );
}
for( auto const &gantry : Gantries ) {
if( false == gantry.removed && gantry.file == File ) { lines.emplace_back( catenary::write_gantry( gantry.data ) ); }
}
for( auto const &anchor : Anchors ) {
if( false == anchor.removed && anchor.file == File ) { lines.emplace_back( catenary::write_anchor( anchor.data ) ); }
}
for( auto const &chain : Chains ) {
if( false == chain.removed && chain.file == File ) { lines.emplace_back( catenary::write_chain( chain.data ) ); }
}
return lines;
}
void void
catenary_scene_file_included( std::string const &File ) { catenary_scene_file_included( std::string const &File ) {
if( auto *file { file_named( File ) }; file != nullptr ) { file->created = false; } if( auto *file { file_named( File ) }; file != nullptr ) { file->created = false; }
@@ -2138,7 +2241,8 @@ catenary_scene_save( std::string &Message ) {
std::vector<std::string> written; std::vector<std::string> written;
for( auto &file : Files ) { for( auto &file : Files ) {
if( false == file.changed ) { continue; } // wpisy w pliku scenerii zapisuje zapis scenerii
if( false == file.changed || InlineFiles.count( file.name ) != 0 ) { continue; }
// wpisy w kolejności listy; usunięte pomijane. CRLF jak w pozostałych plikach scenerii // wpisy w kolejności listy; usunięte pomijane. CRLF jak w pozostałych plikach scenerii
std::string text; std::string text;
std::istringstream header { file.header }; std::istringstream header { file.header };
@@ -2200,6 +2304,8 @@ catenary_scene_new_chain_name( std::string const &File ) {
auto stem { File }; auto stem { File };
if( auto const slash { stem.find_last_of( "/\\" ) }; slash != std::string::npos ) { stem.erase( 0, slash + 1 ); } if( auto const slash { stem.find_last_of( "/\\" ) }; slash != std::string::npos ) { stem.erase( 0, slash + 1 ); }
if( auto const dot { stem.rfind( '.' ) }; dot != std::string::npos ) { stem.erase( dot ); } if( auto const dot { stem.rfind( '.' ) }; dot != std::string::npos ) { stem.erase( dot ); }
// małymi literami: nazwy wpisów node w scenerii parser czyta małymi literami
std::transform( stem.begin(), stem.end(), stem.begin(), []( unsigned char const C ) { return static_cast<char>( std::tolower( C ) ); } );
for( int number = 1; ; ++number ) { for( int number = 1; ; ++number ) {
auto const name { stem + "_l" + std::to_string( number ) }; auto const name { stem + "_l" + std::to_string( number ) };
auto const taken { std::any_of( Chains.begin(), Chains.end(), [ & ]( scene_chain const &Chain ) { return Chain.data.name == name; } ) }; auto const taken { std::any_of( Chains.begin(), Chains.end(), [ & ]( scene_chain const &Chain ) { return Chain.data.name == name; } ) };
@@ -2389,6 +2495,8 @@ catenary_scene_new_gantry_name( std::string const &File ) {
auto stem { File }; auto stem { File };
if( auto const slash { stem.find_last_of( "/\\" ) }; slash != std::string::npos ) { stem.erase( 0, slash + 1 ); } if( auto const slash { stem.find_last_of( "/\\" ) }; slash != std::string::npos ) { stem.erase( 0, slash + 1 ); }
if( auto const dot { stem.rfind( '.' ) }; dot != std::string::npos ) { stem.erase( dot ); } if( auto const dot { stem.rfind( '.' ) }; dot != std::string::npos ) { stem.erase( dot ); }
// małymi literami: nazwy wpisów node w scenerii parser czyta małymi literami
std::transform( stem.begin(), stem.end(), stem.begin(), []( unsigned char const C ) { return static_cast<char>( std::tolower( C ) ); } );
for( int number = 1; ; ++number ) { for( int number = 1; ; ++number ) {
auto const name { stem + "_b" + std::to_string( number ) }; auto const name { stem + "_b" + std::to_string( number ) };
if( std::none_of( Gantries.begin(), Gantries.end(), [ & ]( scene_gantry const &Gantry ) { return Gantry.data.name == name; } ) ) { return name; } if( std::none_of( Gantries.begin(), Gantries.end(), [ & ]( scene_gantry const &Gantry ) { return Gantry.data.name == name; } ) ) { return name; }

View File

@@ -18,6 +18,9 @@ class cParser;
// obsługa dyrektywy "catenary" ze scenerii: wczytuje wskazany plik podwieszeń // obsługa dyrektywy "catenary" ze scenerii: wczytuje wskazany plik podwieszeń
void catenary_scene_load( cParser &Input ); void catenary_scene_load( cParser &Input );
// wpis "node <max> <min> <nazwa> suspension|chain|gantry|anchor ... end<typ>" 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 );
// po wczytaniu scenerii: wiąże podwieszenia z konstrukcjami wsporczymi i przęsłami po nazwach, zgłasza braki // po wczytaniu scenerii: wiąże podwieszenia z konstrukcjami wsporczymi i przęsłami po nazwach, zgłasza braki
void catenary_scene_resolve(); void catenary_scene_resolve();
// przed wczytaniem nowej scenerii: usuwa podwieszenia poprzedniej // przed wczytaniem nowej scenerii: usuwa podwieszenia poprzedniej
@@ -73,6 +76,14 @@ bool catenary_scene_changed();
std::vector<std::string> catenary_scene_new_files(); std::vector<std::string> catenary_scene_new_files();
// sceneria dołącza już plik (edytor dopisał dyrektywę "catenary") // sceneria dołącza już plik (edytor dopisał dyrektywę "catenary")
void catenary_scene_file_included( std::string const &File ); void catenary_scene_file_included( std::string const &File );
// wpisy pliku są zwykłymi wpisami node w pliku scenerii o tej nazwie (warstwie): zapisuje je zapis scenerii, bez dyrektywy "catenary"
bool catenary_scene_file_inline( std::string const &File );
void catenary_scene_file_inline( std::string const &File, bool Inline );
bool catenary_scene_file_changed( std::string const &File );
// zapis scenerii zapisał wpisy pliku
void catenary_scene_file_saved( std::string const &File );
// wpisy pliku jako linie tekstu (wpisy node), w kolejności, w jakiej sceneria ma je wczytać
std::vector<std::string> catenary_scene_entries( std::string const &File );
// warstwa scenerii (uchwyt scene::Layers), której plik dołącza plik sieci, a dla pliku założonego w edytorze ta, do której ma // warstwa scenerii (uchwyt scene::Layers), której plik dołącza plik sieci, a dla pliku założonego w edytorze ta, do której ma
// trafić jego dyrektywa; 0 gdy nieznana (wtedy plik scenariusza) // trafić jego dyrektywa; 0 gdy nieznana (wtedy plik scenariusza)
std::size_t catenary_scene_file_layer( std::string const &File ); std::size_t catenary_scene_file_layer( std::string const &File );