mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 04:29:29 +01:00
feat(editor): undo and redo take back the kept alignments and switches together with the paths: each step of the history keeps the //$h and //$r records from before it; a deleted switch or slip forgets its record in the same step, so undoing the deletion brings it back; edits of the switch number, rail and km and forgetting a kept design stay outside the history; self-test commands deleting paths
This commit is contained in:
@@ -2907,6 +2907,7 @@ bool editor_mode::save()
|
||||
if (m_profile.changed)
|
||||
profile_store();
|
||||
switch_store::tidy();
|
||||
switch_store::settle();
|
||||
infra_store();
|
||||
if (false == m_saved_camera.has_value() || glm::distance(m_saved_camera->first, glm::dvec3{Camera.Pos}) > 0.5 || glm::distance(m_saved_camera->second, glm::vec3{Camera.Angle}) > 0.01f)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "editor/editorOrthophoto.hpp"
|
||||
#include "editor/editorTrack.hpp"
|
||||
#include "editor/editorAlignment.hpp"
|
||||
#include "editor/editorAlignmentStore.hpp"
|
||||
#include "editor/editorSwitchStore.hpp"
|
||||
#include "editor/editorSpeed.hpp"
|
||||
#include "editor/editorRoad.hpp"
|
||||
#include "editor/editorProfile.hpp"
|
||||
@@ -168,6 +170,8 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
std::vector<infra::object_state> infra; // objects which followed the paths
|
||||
bool profiles{false}; // the stored grade line designs changed too, these were the ones before
|
||||
std::vector<stored_profile> profile_library;
|
||||
std::optional<std::vector<alignment_store::record>> alignments; // the kept horizontal alignments changed too, these were the ones before
|
||||
std::optional<std::vector<switch_store::record>> switch_records; // the kept switches changed too, these were the ones before
|
||||
std::vector<array_copy> copies; // models an array added to the one it was made of
|
||||
bool joined{false}; // undone and redone together with the change before it
|
||||
std::shared_ptr<terrain_streamer::change const> terrain; // the heightmap terrain from before the change (Action::Terrain)
|
||||
@@ -630,6 +634,8 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
static bool tracks_editable(std::vector<TTrack *> const &Tracks, std::string &Reason);
|
||||
void trim_history();
|
||||
void restore_track_snapshot(EditorSnapshot const &Snapshot, std::vector<EditorSnapshot> &Opposite, bool const Undo);
|
||||
// changes of the kept alignments and switches go into the last step of the history, made just before them
|
||||
void records_to_history();
|
||||
editor_track::point_ref m_track_point;
|
||||
std::vector<TTrack *> m_track_drag;
|
||||
std::vector<std::pair<TTrack *, editor_track::point_ref>> m_track_drag_points;
|
||||
|
||||
@@ -104,6 +104,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
// switchplace <template> <x> <z> <direction> <side> the catalogue template whose label starts so put into the straight
|
||||
// nearest the point, or into the curve (switchcurve); slipplace <template> <x> <z>
|
||||
// switches logs the switches kept in the scenery and the speed of each diverging track
|
||||
// deletetrack <name>, deletekind <kind> the path deleted as Del does it, or the first path over an element of the kind
|
||||
// btfcheck <terrain file> the .txtf converted to .btf, the editor lines the binary file keeps logged
|
||||
// screenshot
|
||||
// save
|
||||
@@ -1199,6 +1200,36 @@ void editor_mode::selftest_step()
|
||||
}
|
||||
WriteLog("SELFTEST " + command + ": " + tool.status + " " + m_straights.status);
|
||||
}
|
||||
else if (command == "deletetrack" || command == "deletekind")
|
||||
{
|
||||
std::string name;
|
||||
words >> name;
|
||||
TTrack *chosen{nullptr};
|
||||
if (command == "deletetrack")
|
||||
chosen = simulation::Paths.find(name);
|
||||
else
|
||||
for (auto *track : simulation::Paths.sequence())
|
||||
{
|
||||
if (track == nullptr || track->m_editorremoved || track->eType != tt_Normal || track->m_paths.empty())
|
||||
continue;
|
||||
auto const found{alignment_store::on_track(*track)};
|
||||
if (found.has_value() && alignment_store::describe(alignment_store::records()[found->record].result.elements[found->element]).kind == name)
|
||||
{
|
||||
chosen = track;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (chosen == nullptr || chosen->m_editorremoved)
|
||||
{
|
||||
WriteLog("SELFTEST " + command + ": no path " + name);
|
||||
continue;
|
||||
}
|
||||
m_node = chosen;
|
||||
ui()->set_node(m_node);
|
||||
auto const deleted{chosen->name()};
|
||||
delete_selected_track();
|
||||
WriteLog("SELFTEST " + command + ": " + deleted + " deleted");
|
||||
}
|
||||
else if (command == "btfcheck")
|
||||
{
|
||||
std::string name;
|
||||
|
||||
@@ -1233,10 +1233,25 @@ void editor_mode::push_track_snapshot(std::vector<std::pair<TTrack *, editor_tra
|
||||
snap.created = std::move(Created);
|
||||
snap.removed = std::move(Removed);
|
||||
infra_attach(snap);
|
||||
snap.alignments = alignment_store::take_changes();
|
||||
snap.switch_records = switch_store::take_changes();
|
||||
m_history.push_back(std::move(snap));
|
||||
g_redo.clear();
|
||||
}
|
||||
|
||||
void editor_mode::records_to_history()
|
||||
{
|
||||
auto alignments{alignment_store::take_changes()};
|
||||
auto switches{switch_store::take_changes()};
|
||||
if (m_history.empty() || m_history.back().action != EditorSnapshot::Action::TrackEdit)
|
||||
return;
|
||||
auto &last{m_history.back()};
|
||||
if (alignments.has_value() && false == last.alignments.has_value())
|
||||
last.alignments = std::move(alignments);
|
||||
if (switches.has_value() && false == last.switch_records.has_value())
|
||||
last.switch_records = std::move(switches);
|
||||
}
|
||||
|
||||
bool editor_mode::tracks_editable(std::vector<TTrack *> const &Tracks, std::string &Reason)
|
||||
{
|
||||
for (auto const *track : Tracks)
|
||||
@@ -1282,6 +1297,18 @@ void editor_mode::restore_track_snapshot(EditorSnapshot const &Snapshot, std::ve
|
||||
m_profiles = Snapshot.profile_library;
|
||||
profile_library_mark(std::move(layers));
|
||||
}
|
||||
alignment_store::settle();
|
||||
switch_store::settle();
|
||||
if (Snapshot.alignments.has_value())
|
||||
{
|
||||
current.alignments = alignment_store::records();
|
||||
alignment_store::restore(*Snapshot.alignments);
|
||||
}
|
||||
if (Snapshot.switch_records.has_value())
|
||||
{
|
||||
current.switch_records = switch_store::records();
|
||||
switch_store::restore(*Snapshot.switch_records);
|
||||
}
|
||||
Opposite.push_back(std::move(current));
|
||||
|
||||
if (Undo)
|
||||
@@ -1835,6 +1862,7 @@ void editor_mode::render_turnout_ui()
|
||||
editor_track::follow(*track, following);
|
||||
editor_track::commit(changed);
|
||||
record_switch(*track, Template);
|
||||
records_to_history();
|
||||
if (false == loose.empty())
|
||||
ui()->set_status(STR_C("Too far from the new ends of the switch, left unjoined: ") + loose, true);
|
||||
};
|
||||
@@ -2552,6 +2580,7 @@ void editor_mode::render_route_stored()
|
||||
if (ImGui::SmallButton(STR_C("Forget the kept design")))
|
||||
{
|
||||
alignment_store::forget(route.departed);
|
||||
alignment_store::settle();
|
||||
route.departed = -1;
|
||||
route.status = STR_C("The kept design will be removed from ") + layer + STR_C(" on save");
|
||||
}
|
||||
@@ -2711,6 +2740,7 @@ void editor_mode::route_apply()
|
||||
route_update();
|
||||
}
|
||||
route_store();
|
||||
records_to_history();
|
||||
route.status = STR_C("Re-laid ") + std::to_string(chain.tracks.size()) + STR_C(" paths") + (added > 0 ? STR_C(", added ") + std::to_string(added) : std::string{});
|
||||
if (startextension > 0.0 || endextension > 0.0)
|
||||
route.status += format(", the curve took over %.2f / %.2f m of the adjoining straights", startextension, endextension);
|
||||
@@ -4313,6 +4343,8 @@ void editor_mode::delete_selected_track()
|
||||
return;
|
||||
}
|
||||
// retired first, so the infrastructure it carried and moves elsewhere is in the same undo step
|
||||
if (auto const known{switch_store::of(*track)})
|
||||
switch_store::forget(known->name);
|
||||
editor_track::retire(*track);
|
||||
// a switch leaves its main track behind, so the line isn't broken, unless Shift asks for all of it
|
||||
std::vector<TTrack *> created;
|
||||
@@ -6265,7 +6297,10 @@ void editor_mode::render_switch_record(TTrack &Track)
|
||||
item_tooltip("Chainage of the start of the switch; taken from the horizontal alignment kept in the scenery when the switch lies on one");
|
||||
ImGui::PopItemWidth();
|
||||
if (changed)
|
||||
{
|
||||
switch_store::put(entry, entry.layer);
|
||||
switch_store::settle();
|
||||
}
|
||||
}
|
||||
|
||||
void editor_mode::switch_placed(std::vector<TTrack *> const &Created)
|
||||
|
||||
@@ -33,6 +33,8 @@ std::string const kMark{"//$h"};
|
||||
double const kStep{1.0};
|
||||
|
||||
std::vector<record> g_records;
|
||||
std::vector<record> g_base; // as they were before the changes not yet in the undo history
|
||||
bool g_changed{false};
|
||||
bool g_loaded{false};
|
||||
|
||||
// format of the lines, coordinates in the space of the file:
|
||||
@@ -257,6 +259,8 @@ stored_element describe(alignment::element const &Element)
|
||||
void reset()
|
||||
{
|
||||
g_records.clear();
|
||||
g_base.clear();
|
||||
g_changed = false;
|
||||
g_loaded = false;
|
||||
}
|
||||
|
||||
@@ -363,6 +367,8 @@ void load()
|
||||
}
|
||||
}
|
||||
}
|
||||
g_base = g_records;
|
||||
g_changed = false;
|
||||
if (false == g_records.empty())
|
||||
WriteLog("Editor: " + std::to_string(g_records.size()) + " horizontal alignment(s) read from the scenery", logtype::generic);
|
||||
}
|
||||
@@ -585,6 +591,7 @@ int store(alignment::design const &Design, alignment::result const &Result, doub
|
||||
g_records.push_back(std::move(entry));
|
||||
index = static_cast<int>(g_records.size()) - 1;
|
||||
}
|
||||
g_changed = true;
|
||||
mark(g_records[index].layer);
|
||||
return index;
|
||||
}
|
||||
@@ -596,6 +603,7 @@ void forget(int const Index)
|
||||
return;
|
||||
auto const layer{g_records[Index].layer};
|
||||
g_records.erase(g_records.begin() + Index);
|
||||
g_changed = true;
|
||||
mark(layer);
|
||||
}
|
||||
|
||||
@@ -607,4 +615,32 @@ std::optional<double> chainage_at(glm::dvec3 const &Point)
|
||||
return g_records[found->record].origin + found->chainage;
|
||||
}
|
||||
|
||||
std::optional<std::vector<record>> take_changes()
|
||||
{
|
||||
if (false == g_changed)
|
||||
return std::nullopt;
|
||||
auto before{std::move(g_base)};
|
||||
g_base = g_records;
|
||||
g_changed = false;
|
||||
return before;
|
||||
}
|
||||
|
||||
void settle()
|
||||
{
|
||||
g_base = g_records;
|
||||
g_changed = false;
|
||||
}
|
||||
|
||||
void restore(std::vector<record> Records)
|
||||
{
|
||||
std::set<scene::layer_handle> layers;
|
||||
for (auto const *list : {&g_records, &Records})
|
||||
for (auto const &entry : *list)
|
||||
layers.insert(entry.layer);
|
||||
g_records = std::move(Records);
|
||||
for (auto const layer : layers)
|
||||
mark(layer);
|
||||
settle();
|
||||
}
|
||||
|
||||
} // namespace alignment_store
|
||||
|
||||
@@ -87,5 +87,11 @@ int store(alignment::design const &Design, alignment::result const &Result, doub
|
||||
void forget(int const Index);
|
||||
// km of the point on the stored alignments, if it lies on one
|
||||
std::optional<double> chainage_at(glm::dvec3 const &Point);
|
||||
// the records as they were before the changes made since the last call, if there were any; the current ones become the base
|
||||
std::optional<std::vector<record>> take_changes();
|
||||
// the current records become the base, the changes made so far aren't a part of the undo history
|
||||
void settle();
|
||||
// puts the records back as they were, for undo and redo
|
||||
void restore(std::vector<record> Records);
|
||||
|
||||
} // namespace alignment_store
|
||||
|
||||
@@ -33,6 +33,8 @@ std::string const kMark{"//$r"};
|
||||
double const kTolerance{0.02};
|
||||
|
||||
std::vector<record> g_records;
|
||||
std::vector<record> g_base; // as they were before the changes not yet in the undo history
|
||||
bool g_changed{false};
|
||||
bool g_loaded{false};
|
||||
|
||||
// format of the lines, coordinates in the space of the file:
|
||||
@@ -100,6 +102,8 @@ void mark(scene::layer_handle const Layer)
|
||||
void reset()
|
||||
{
|
||||
g_records.clear();
|
||||
g_base.clear();
|
||||
g_changed = false;
|
||||
g_loaded = false;
|
||||
}
|
||||
|
||||
@@ -143,6 +147,8 @@ void load()
|
||||
g_records.push_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
g_base = g_records;
|
||||
g_changed = false;
|
||||
if (false == g_records.empty())
|
||||
WriteLog("Editor: " + std::to_string(g_records.size()) + " switch record(s) read from the scenery", logtype::generic);
|
||||
}
|
||||
@@ -189,11 +195,13 @@ void put(record Entry, scene::layer_handle const Layer)
|
||||
if (Entry.number == "-")
|
||||
Entry.number = existing->number;
|
||||
*existing = std::move(Entry);
|
||||
g_changed = true;
|
||||
mark(existing->layer);
|
||||
return;
|
||||
}
|
||||
Entry.layer = Layer;
|
||||
g_records.push_back(std::move(Entry));
|
||||
g_changed = true;
|
||||
mark(g_records.back().layer);
|
||||
}
|
||||
|
||||
@@ -204,7 +212,9 @@ void forget(std::string const &Name)
|
||||
for (auto const &entry : g_records)
|
||||
if (entry.name == Name)
|
||||
layers.insert(entry.layer);
|
||||
auto const count{g_records.size()};
|
||||
g_records.erase(std::remove_if(g_records.begin(), g_records.end(), [&](record const &Entry) { return Entry.name == Name; }), g_records.end());
|
||||
g_changed = g_changed || g_records.size() != count;
|
||||
for (auto const layer : layers)
|
||||
mark(layer);
|
||||
}
|
||||
@@ -234,6 +244,7 @@ void tidy()
|
||||
g_records.erase(std::remove_if(g_records.begin(), g_records.end(), gone), g_records.end());
|
||||
for (auto const layer : layers)
|
||||
mark(layer);
|
||||
g_changed = g_changed || false == layers.empty();
|
||||
}
|
||||
|
||||
std::string label(record const &Record)
|
||||
@@ -254,4 +265,32 @@ std::string label(record const &Record)
|
||||
return text;
|
||||
}
|
||||
|
||||
std::optional<std::vector<record>> take_changes()
|
||||
{
|
||||
if (false == g_changed)
|
||||
return std::nullopt;
|
||||
auto before{std::move(g_base)};
|
||||
g_base = g_records;
|
||||
g_changed = false;
|
||||
return before;
|
||||
}
|
||||
|
||||
void settle()
|
||||
{
|
||||
g_base = g_records;
|
||||
g_changed = false;
|
||||
}
|
||||
|
||||
void restore(std::vector<record> Records)
|
||||
{
|
||||
std::set<scene::layer_handle> layers;
|
||||
for (auto const *list : {&g_records, &Records})
|
||||
for (auto const &entry : *list)
|
||||
layers.insert(entry.layer);
|
||||
g_records = std::move(Records);
|
||||
for (auto const layer : layers)
|
||||
mark(layer);
|
||||
settle();
|
||||
}
|
||||
|
||||
} // namespace switch_store
|
||||
|
||||
@@ -57,5 +57,11 @@ void forget(std::string const &Name);
|
||||
void tidy();
|
||||
// "Rz 60E1-500-1:12 L", as the type is written on the lists of the switches
|
||||
std::string label(record const &Record);
|
||||
// the records as they were before the changes made since the last call, if there were any; the current ones become the base
|
||||
std::optional<std::vector<record>> take_changes();
|
||||
// the current records become the base, the changes made so far aren't a part of the undo history
|
||||
void settle();
|
||||
// puts the records back as they were, for undo and redo
|
||||
void restore(std::vector<record> Records);
|
||||
|
||||
} // namespace switch_store
|
||||
|
||||
Reference in New Issue
Block a user