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:
@@ -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)
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 ) };
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|||||||
Reference in New Issue
Block a user