/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http: */ #pragma once #include #include #include #include #include #include #include #include "world/Segment.h" #include "editor/editorAlignment.hpp" #include "utilities/Classes.h" class TTrack; class basic_event; class editor_track { public: enum class point_kind { start, control1, control2, end }; static bool is_end(point_kind const Kind) { return Kind == point_kind::start || Kind == point_kind::end; } struct point_ref { int path{-1}; point_kind kind{point_kind::start}; bool valid() const { return path >= 0; } bool operator==(point_ref const &Other) const { return path == Other.path && kind == Other.kind; } }; struct state { std::vector paths; double velocity{-1.0}; float switchvelocity{-1.f}; float width{1.435f}; float friction{0.15f}; float sounddistance{-1.f}; int quality{20}; int damage{0}; int environment{0}; material_handle material1{0}; material_handle material2{0}; material_handle material3{0}; float texlength{4.f}; float texheight{0.6f}; float texwidth{0.9f}; float texslope{0.9f}; std::vector isolated; float overhead{-1.f}; bool sleepers{false}; float sleeper_frequency{0.6f}; std::string sleeper_model; std::string sleeper_skin; glm::vec3 sleeper_offset{0.f}; float sleeper_ballast{0.f}; std::array>, 6> events; std::string name; }; static state capture(TTrack const &Track); static void apply(TTrack &Track, state const &State); static bool name_valid(TTrack const &Track, std::string &Name, std::string &Reason, TTrack **Owner = nullptr); struct name_uses { int cells{0}; int includes{0}; std::vector events; std::vector loose; }; static name_uses uses_of_name(TTrack const &Track); static void rename(TTrack &Track, std::string const &Name); // lists of the events of the path, in the order of event_keyword(): event0, event1, event2, eventall0, eventall1, eventall2 static std::vector> &events(TTrack &Track, int const Index); static char const *event_keyword(int const Index); // looks the events up by their names; the names of those which don't exist are given in Missing static void bind_events(TTrack &Track, std::string &Missing); // track circuits the path belongs to, made when they don't exist yet static void isolated(TTrack &Track, std::vector const &Names); // -1: normal, 0: no current, above 0: speed limit with the pantographs lowered static void overhead(TTrack &Track, float const Overhead); static bool can_edit_geometry(TTrack const &Track, std::string &Reason); static bool is_supported(TTrack const &Track); static void commit(std::vector const &Tracks); static void commit_parameters(TTrack &Track); static glm::dvec3 point_position(TTrack const &Track, point_ref const &Point); static void move_point(TTrack &Track, point_ref const &Point, glm::dvec3 const &Position); static void translate(TTrack &Track, glm::dvec3 const &Offset); static void rotate_y(TTrack &Track, glm::dvec3 const &Pivot, double const Angle); static glm::dvec3 pivot(TTrack const &Track); static std::vector neighbours(TTrack const &Track); static std::vector> connected_points(TTrack const &Track, glm::dvec3 const &Position); static bool is_connected(TTrack const &Track, point_ref const &Point); // end of a regular neighbouring path joined to an end of the track, as it was when taken struct joint { point_ref own; TTrack *other{nullptr}; point_ref theirs; glm::dvec2 direction{0.0, 1.0}; // of the track at its end, into it glm::dvec3 control{0.0}; // of the neighbour at its end, never zero }; // joints the editor can change: switches and paths which can't be edited aren't taken static std::vector joints(TTrack const &Track); // moves the joined ends to where the ends of the track are now, turning them as the track turned there static void follow(TTrack const &Track, std::vector const &Joints); static void follow_grade(TTrack const &Track, std::vector const &Joints); static void conform_switch(TTrack &Track); static double switch_station(TTrack const &Track, glm::dvec3 const &Position); static std::vector hold_controls(TTrack &Track); static void settle_controls(TTrack &Track, std::vector const &Paths); static std::vector> partners(TTrack &Track, point_ref const &End); static void align_controls(TTrack const &Track, point_ref const &Control, std::vector> const &Partners); static point_ref end_of(point_ref const &Control); struct snap_target { TTrack *track{nullptr}; point_ref point; glm::dvec3 position{0.0}; glm::dvec3 direction{0.0}; double distance{0.0}; }; static snap_target find_snap_target(TTrack const &Track, glm::dvec3 const &Position, double const Radius, std::vector const &Exclude); // nearest end of a path of the category which nothing else joins; Self may be null static snap_target find_free_end(TTrack const *Self, int const Category, glm::dvec3 const &Position, double const Radius, std::vector const &Exclude); // all of them within the radius, nearest first static std::vector free_ends(TTrack const *Self, int const Category, glm::dvec3 const &Position, double const Radius, std::vector const &Exclude); static void snap_point(TTrack &Track, point_ref const &Point, snap_target const &Target, bool const Aligntangent); struct chain { std::vector tracks; std::vector forward; glm::dvec3 start{0.0}; glm::dvec3 end{0.0}; glm::dvec3 start_direction{0.0}; glm::dvec3 end_direction{0.0}; double start_radius{0.0}; double end_radius{0.0}; double length{0.0}; double velocity{-1.0}; double radius{0.0}; // switches the chain runs through along their main track, each before the regular path of the index given struct passage { TTrack *track{nullptr}; bool forward{true}; std::size_t before{0}; }; std::vector switches; }; // with Switches the chain may run through the switches along their main tracks; its ends are regular paths static bool find_chain(TTrack *From, TTrack *To, chain &Chain, std::string &Error, bool const Switches = false); // the regular parts of a chain between its switches, empty ones skipped static std::vector chain_parts(chain const &Chain); // moves the track as a whole, From going to To, turned by Angle in the plan static void place(TTrack &Track, glm::dvec3 const &From, glm::dvec3 const &To, double const Angle); struct straight { std::vector tracks; std::vector paths; glm::dvec3 start{0.0}; glm::dvec3 end{0.0}; glm::dvec2 direction{0.0, 1.0}; double length{0.0}; double grade{0.0}; double azimuth{0.0}; // distance of the point from the start, measured along the line in the plan double along(glm::dvec3 const &Point) const { return (Point.x - start.x) * direction.x + (Point.z - start.z) * direction.y; } }; struct straight_tolerance { double angle{0.0002}; double offset{0.02}; double radius{20000.0}; }; static bool is_straight(TTrack const &Track, straight_tolerance const &Tolerance); static straight find_straight(TTrack &Track, straight_tolerance const &Tolerance); static std::vector find_straights(double const Minimumlength, straight_tolerance const &Tolerance); static std::vector straight_affected(std::vector const &Lines); static TTrack *outside_neighbour(straight const &Line, bool const Atend); static bool touches(TTrack const &Track, glm::dvec3 const &Point); struct curve { TTrack *from{nullptr}; TTrack *to{nullptr}; double turn{0.0}; int reversals{0}; double radius{0.0}; double transition_in{0.0}; double transition_out{0.0}; double cant{0.0}; bool compound{false}; struct arc_part { double radius{0.0}; double turn{0.0}; double transition{0.0}; }; std::vector arcs; // the parameters of the vertex in the alignment kept in the scenery, in the direction of the run, when the paths follow one std::optional designed; }; static bool find_curve(TTrack &Track, straight_tolerance const &Tolerance, double const Gauge, curve &Curve); // curve made of the given paths, each with the flag of being run from its start static bool analyse_curve(std::vector> const &Run, double const Gauge, curve &Curve); // regular paths in line through the track, up to switches, free ends or about Limit metres each way static bool find_run(TTrack &Track, double const Limit, chain &Chain, std::string &Error); // the chain read as an alignment: a vertex at the intersection of the tangents of each curve struct line_vertex { glm::dvec2 position{0.0}; curve shape; bool kink{false}; // the straights meet with no curve between them }; static std::vector recognize_line(chain const &Chain, straight_tolerance const &Tolerance, double const Gauge); // regular straight paths continuing a chain end in line, which the chain can take over struct straight_run { std::vector tracks; std::vector outward; double length{0.0}; }; static straight_run straight_beyond(chain const &Chain, bool const Atend, straight_tolerance const &Tolerance); static double run_reserve(straight_run const &Run); static TTrack *take_straight(straight_run const &Run, TTrack *Edge, glm::dvec3 const &Joint, glm::dvec3 const &Cut, std::vector> &States, std::vector &Created); // sequence of paths, switches included, which a train can run along struct route_span { TTrack *track{nullptr}; int path{0}; bool forward{true}; double from{0.0}; // chainage, measured in the plan double to{0.0}; }; struct route { std::vector spans; double length{0.0}; }; static bool find_route(TTrack *From, TTrack *To, route &Route, std::string &Error); // continues both ways from the path, through switches along their path 0 static route run_route(TTrack &Track, double const Maximum); static route route_of(chain const &Chain); struct route_sample { double chainage{0.0}; glm::dvec3 position{0.0}; glm::dvec2 direction{0.0, 1.0}; double grade{0.0}; double curvature{0.0}; // signed, 1/m, positive turning left double cant{0.0}; // m, raise of the outer rail std::size_t span{0}; }; static std::vector sample_route(route &Route, double const Step); static double sampled_elevation(std::vector const &Samples, double const Chainage); static double sampled_grade(std::vector const &Samples, double const Chainage); static glm::dvec3 sampled_position(std::vector const &Samples, double const Chainage); // grade of the path which adjoins the end of the route, positive rising along the route static bool adjoining_grade(route const &Route, bool const Atend, double &Grade); // path off the route whose end no longer met the adjoining path in height struct height_gap { TTrack *track{nullptr}; TTrack *neighbour{nullptr}; double gap{0.0}; // neighbour - track bool closed{false}; // the end of the neighbour was brought to the track }; // sets elevations of the paths of the route; regular paths are split at the chainages in Breaks, // switches are tilted as a whole in the plane of their grade, branches included, and the regular paths // off the route get their ends joined to the switches brought to the height there static std::vector apply_profile(route &Route, std::function const &Elevation, std::function const &Grade, std::vector const &Breaks, std::vector> &States, std::vector &Created); // carries elevations of the chain over to the new pieces, in proportion of the length static void keep_heights(chain const &Chain, std::vector &Pieces); struct switch_template { TTrack *source{nullptr}; std::array local; double length{0.0}; double ratio{0.0}; double radius{0.0}; int count{0}; std::string label; double a{0.0}; double b{0.0}; bool double_slip{false}; std::string type{"Rz"}; std::string rail{"-"}; }; static std::vector standard_switch_templates(); static std::vector find_switch_templates(); static TTrack *load_path(std::string const &Text, TTrack const &Template, std::string const &Name = {}); static TTrack *create_path(TTrack const &Style, segment_data const &Path); // parameters of a path laid where there's no other path to copy them from struct path_style { double velocity{100.0}; std::string rail{"rail_screw_used1"}; std::string ballast{"1435mm/tpbps-new2"}; std::string rail_profile; }; // the path goes to the layer receiving the items created in the editor static TTrack *create_path(path_style const &Style, segment_data const &Path); static TTrack *split_path(TTrack &Track, double const T); static double nearest_parameter(TTrack const &Track, glm::dvec3 const &Point); // point of the first path at the parameter of nearest_parameter() static glm::dvec3 point_at(TTrack const &Track, double const T); static std::vector place_switch(switch_template const &Template, glm::dvec3 const &Origin, glm::dvec2 const &Direction, int const Side, double const Grade); static TTrack *create_switch(switch_template const &Template, std::vector const &Paths, TTrack const &Style, std::string const &Name = {}); static TTrack *create_turntable(segment_data const &Path, TTrack const *Style, std::string const &Name); static void move_straights(std::vector const &Lines, std::vector> const &Ends); static std::vector relay(chain const &Chain, std::vector const &Pieces); static void retire(TTrack &Track); static std::string material_name(material_handle const Material); static material_handle fetch_material(std::string const &Name); static float texture_height(TTrack const &Track); static void texture_height(TTrack &Track, float const Height); static double velocity(TTrack const &Track); static void velocity(TTrack &Track, double const Velocity); static void damage(TTrack &Track, int const Damage); // told about the changes of the paths, to let the objects which belong to them follow class observer { public: virtual ~observer() = default; virtual void track_captured(TTrack const &Track) = 0; virtual void tracks_committed(std::vector const &Tracks) = 0; virtual void track_retired(TTrack &Track) = 0; // called before the commit; Original keeps the beginning of the path, given in First virtual void track_split(TTrack &Original, TTrack &Created, segment_data const &First) = 0; }; static void observe(observer *Observer) { s_observer = Observer; } private: static observer *s_observer; // sets the length, the ends and their directions of the chain of the tracks given static bool complete_chain(chain &Chain, std::string &Error); static void disconnect(TTrack &Track); static void join(TTrack &Track); static void connect(TTrack &Track, bool const Prevside, TTrack *Other, int const Endpointid); static void update_transition(TTrack &Track); static void bind_switch_path(TTrack &Switch, int const Path); static void store_switch_path(TTrack &Switch, int const Path); static void rebuild_geometry(TTrack &Track); static TTrack *clone(TTrack const &Template); static TTrack *load_text(std::string const &Text, TTrack const *Template, std::string const &Name); static void move_straight(straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End, std::function const &Member); };