17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-28 15:47:30 +01:00

feat(tracks): track editor in one movable Tracks window with exclusive modes, a guide of what can be done in the current step and the rarely used fields folded; vertical profile window with the ground of the scenery triangles; plain pieces added at a free end, freehand only on request; curve mode edits the single curve clicked

This commit is contained in:
maj00r
2026-10-05 01:21:11 +02:00
parent 220fd0f37e
commit 3731520e33
13 changed files with 2769 additions and 924 deletions

View File

@@ -21,6 +21,7 @@ http://mozilla.org/MPL/2.0/.
#include "editor/editorOrthophoto.hpp"
#include "editor/editorTrack.hpp"
#include "editor/editorAlignment.hpp"
#include "editor/editorSpeed.hpp"
#include "editor/editorRoad.hpp"
#include "editor/editorProfile.hpp"
#include "editor/editorInfra.hpp"
@@ -103,6 +104,7 @@ class editor_mode : public application_mode, private editor_track::observer
bool picking;
};
struct stored_profile;
struct EditorSnapshot
{
enum class Action { Move, Rotate, Scale, Add, Delete, TrackEdit, Other, RoadEdit };
@@ -123,6 +125,8 @@ class editor_mode : public application_mode, private editor_track::observer
std::vector<TTrack *> removed;
editor_road::record roads; // what a change of the roads comes to
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;
};
void push_snapshot(scene::basic_node *node, EditorSnapshot::Action Action = EditorSnapshot::Action::Move, std::string const &Serialized = std::string());
@@ -300,19 +304,22 @@ class editor_mode : public application_mode, private editor_track::observer
float m_gizmo_snap{1.0f}; // translation snap step (metres) applied while Ctrl is held
TTrack *selected_track() const;
void render_track_ui();
void render_path_ui();
void draw_track_overlay() const;
bool pick_track_handle();
void select_track(scene::basic_node *Node);
void render_track_gizmo();
void commit_track_drag(bool const Force);
void cancel_track_tools();
void push_track_snapshot(std::vector<std::pair<TTrack *, editor_track::state>> States, std::vector<TTrack *> Created = {}, std::vector<TTrack *> Removed = {});
// false, with the reason, when one of the paths can't be changed, its file not taking the changes back
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);
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;
std::vector<editor_track::joint> m_track_joints; // of the neighbours, following the whole track dragged
editor_track::snap_target m_track_snap;
glm::dvec3 m_track_pivot{0.0};
glm::mat4 m_track_gizmo{1.0f};
@@ -322,6 +329,15 @@ class editor_mode : public application_mode, private editor_track::observer
editor_track::state m_track_field_before;
std::array<std::array<char, 256>, 3> m_track_materials{};
std::array<TTrack const *, 3> m_track_material_edited{}; // path whose texture name is being typed
// typed names of the track circuits, the sleeper model and its skin, and the event to add, for the path
TTrack const *m_track_names_for{nullptr};
std::array<char, 256> m_track_isolated{};
std::array<char, 256> m_track_sleeper_model{};
std::array<char, 256> m_track_sleeper_skin{};
std::array<char, 128> m_track_event{};
int m_track_event_list{0};
std::string m_track_events_missing;
void render_path_circuits(TTrack &Track);
std::string m_track_mode_notice;
// structure gauge along the line of the selected path, shown in the 3d view
struct gauge_state
@@ -383,9 +399,9 @@ class editor_mode : public application_mode, private editor_track::observer
alignment::result result;
int vertex{-1};
int grip{-1};
bool single{true}; // one curve between its straights, instead of the whole line
};
void render_route_ui();
void render_route_ends();
bool render_route_parameters();
bool render_route_vertices();
bool render_route_vertex(int const Index);
@@ -393,8 +409,12 @@ class editor_mode : public application_mode, private editor_track::observer
void route_reset();
void route_bind_ends();
bool route_from_curve(TTrack &Track);
bool route_from_line(TTrack &Track);
alignment::vertex route_vertex_of(editor_track::curve const &Curve) const;
bool route_load(TTrack &Track) { return m_route.single ? route_from_curve(Track) : route_from_line(Track); }
void route_update() { m_route.result = alignment::compute(m_route.design); }
void route_apply();
bool route_relay_through_switches(editor_track::chain const &Chain, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created);
void route_recommend(alignment::vertex &Vertex) const;
void draw_route_overlay() const;
bool pick_route_vertex();
@@ -489,11 +509,13 @@ class editor_mode : public application_mode, private editor_track::observer
std::vector<editor_track::switch_template> templates;
bool collected{false};
int armed{-1};
int last{0}; // template armed the last time
bool placing{false};
double along{0.0};
glm::dvec3 point{0.0};
glm::dvec3 mouse{0.0};
editor_track::straight line;
std::string status;
};
switch_tool m_switch;
int m_turnout_template{0};
@@ -509,6 +531,31 @@ class editor_mode : public application_mode, private editor_track::observer
bool atend{true};
};
extend_tool m_extend;
// lays new track through the clicked points, with no path needed to start from
struct lay_tool
{
bool active{false};
std::vector<glm::dvec3> points;
editor_track::snap_target start; // free end the track starts from, if any
glm::dvec3 mouse{0.0};
editor_track::snap_target mouse_snap;
std::vector<segment_data> preview;
std::string preview_error;
double preview_length{0.0};
double radius{500.0};
bool transitions{true};
double piece_length{100.0};
bool copy_selected{false};
editor_track::path_style style;
std::string status;
};
lay_tool m_lay;
void render_lay_ui();
void render_lay_status();
void lay_click();
void lay_finish(editor_track::snap_target const &End);
void lay_cancel();
std::vector<segment_data> lay_pieces(std::vector<glm::dvec3> const &Points, editor_track::snap_target const &End, double &Length, std::string &Error) const;
void render_switch_ui();
bool start_switch_placement();
void finish_switch_placement();
@@ -538,17 +585,44 @@ class editor_mode : public application_mode, private editor_track::observer
bool start_extend();
void finish_extend();
std::vector<segment_data> extend_pieces() const;
// the free end of the path as the start of a new track; false if it isn't free or has no direction
static bool free_end(TTrack &Track, int const Path, bool const Atend, extend_tool &Tool);
// the free end new pieces are added at: the selected point if it's one, else the end, else the start
bool chosen_free_end(extend_tool &Tool);
// adds a straight (Side 0) or a curve to the left (1) or the right (-1) at the free end of the selected track
void extend_add(int const Side);
void render_extend_ui();
bool m_extend_freehand{false}; // dragging the ring of a free end draws a curve through the cursor, not a straight
double m_extend_length{25.0};
double m_extend_radius{300.0};
bool extend_snap(editor_track::snap_target &Target) const;
void update_build_tools();
void draw_build_overlay() const;
enum class track_tab { straights, route, path, turnout };
// modes of the track editor, one at a time: path is the plain selection
enum class track_tab { straights, route, path, turnout, profile, speed, infra, lay };
struct track_mode
{
char const *label;
char const *key;
track_tab tab;
char const *tooltip;
};
static std::array<track_mode, 8> const &track_modes();
void render_track_menu();
void arm_switch();
bool m_track_window_open{false};
void render_track_window();
void render_track_modes(TTrack &Track);
// the inspector is pinned to the right edge of the screen, the profile strip to the bottom one, both resized from the inner edge
void render_track_inspector();
void render_track_modes(TTrack *Track);
void show_track_tab(track_tab const Tab);
bool track_analysis_tab() const { return m_track_tab == track_tab::profile || m_track_tab == track_tab::speed || m_track_tab == track_tab::infra; }
bool track_shortcut(int const Key);
bool track_busy() const; // something going on in the mode, which Esc drops before it leaves the mode
std::string track_mode_name() const;
void render_turnout_ui();
void render_path_parameters(TTrack &Track);
void render_straight_ui();
track_tab m_track_tab{track_tab::straights};
track_tab m_track_tab{track_tab::path};
void delete_selected_track();
void start_straight_gesture(int const Tool);
void finish_straight_gesture();
@@ -558,7 +632,8 @@ class editor_mode : public application_mode, private editor_track::observer
char const *key;
std::string action;
};
std::vector<key_hint> track_key_hints() const;
std::vector<key_hint> track_key_hints(bool const All) const;
void render_track_guide();
std::string track_readout() const;
void draw_track_hints();
bool m_route_tab{true};
@@ -593,6 +668,11 @@ class editor_mode : public application_mode, private editor_track::observer
bool panning{false};
bool changed{false};
double hover{-1.0};
// under the cursor in the strip
int hover_point{-1};
int hover_grip{-1};
bool hover_line{false};
int shown{-2}; // point in the side panel
std::string status;
std::string error;
};
@@ -616,7 +696,10 @@ class editor_mode : public application_mode, private editor_track::observer
void profile_forget();
void profile_open_stored(std::size_t const Index);
void profile_edited();
void render_profile_window();
void render_profile_body();
void render_profile_strip();
void render_profile_toolbar();
void render_profile_points();
void render_profile_source();
bool render_profile_parameters();
void render_profile_issues();
@@ -630,7 +713,7 @@ class editor_mode : public application_mode, private editor_track::observer
void profile_fit_view();
void profile_after_undo();
struct profile_view;
void render_profile_canvas();
void render_profile_canvas(float const Height);
void profile_fit_exaggeration(profile_view const &View);
void profile_navigate(profile_view const &View, bool const Hovered);
bool profile_canvas_edit(profile_view const &View, bool const Hovered);
@@ -687,7 +770,32 @@ class editor_mode : public application_mode, private editor_track::observer
void track_split(TTrack &Original, TTrack &Created, segment_data const &First) override;
void infra_move(infra::binding &Binding);
void infra_attach(EditorSnapshot &Snapshot);
void render_infra_window();
void render_infra_body();
// speed limits of the paths checked against what their geometry allows
struct speed_state
{
bool open{false};
int scope{0}; // 0: the line through the selected path, 1: the whole scenery
speed_check::options options;
std::vector<std::pair<TTrack *, int>> queue;
std::size_t next{0};
std::vector<speed_check::verdict> results;
bool show_all{false};
int limits{0}; // 0: all the paths, 1: only those with a speed limit set, 2: only those without
int selected{-1};
std::size_t history{0}; // size of the undo history at the check
std::string status;
};
speed_state m_speed;
void render_speed_body();
void speed_start();
void speed_step();
void speed_focus(int const Index);
// with Branches the limits of the switch branches count too, otherwise they're only warnings
void speed_apply(std::vector<int> const &Indices, bool const Branches);
std::vector<int> speed_listed() const;
void draw_speed_overlay() const;
void infra_rebase();
void render_infra_search();
void render_infra_candidates();
@@ -811,7 +919,8 @@ class editor_mode : public application_mode, private editor_track::observer
// Firstgrade, Lastgrade: slopes the road has to start and end with, nullptr for none
void road_profile(std::vector<segment_data> &Pieces, double const *Firstgrade, double const *Lastgrade, bool const Fresh);
// height of the ground under each of the points, with the roads not taken for the ground. a point with nothing under it keeps its height
std::vector<double> ground_heights(std::vector<glm::dvec3> const &Points, bool const Fresh);
// Found: set for the points which have the ground under them
std::vector<double> ground_heights(std::vector<glm::dvec3> const &Points, bool const Fresh, std::vector<char> *Found = nullptr);
void push_road_snapshot(editor_road::record Record);
void restore_road_snapshot(EditorSnapshot const &Snapshot, std::vector<EditorSnapshot> &Opposite, bool const Undo);
road_tool m_roadtool;