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

chore(editor): cleanup comments

This commit is contained in:
maj00r
2026-10-02 22:28:45 +02:00
parent 8f2a34b3cf
commit 7ede96df5f
12 changed files with 84 additions and 283 deletions

View File

@@ -4,7 +4,7 @@ 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://mozilla.org/MPL/2.0/.
http:
*/
#include "stdafx.h"
@@ -30,7 +30,6 @@ double cross(glm::dvec2 const &A, glm::dvec2 const &B)
return A.x * B.y - A.y * B.x;
}
// vector rotated by 90 degrees counter-clockwise
glm::dvec2 perpendicular(glm::dvec2 const &Vector)
{
return {-Vector.y, Vector.x};
@@ -63,15 +62,13 @@ std::string format(char const *Format, ...)
return buffer;
}
// point of a transition curve in its local frame: x along the tangent at its start, y towards the curve centre
struct local_point
{
double x{0.0};
double y{0.0};
double angle{0.0}; // tangent angle relative to the initial tangent
double angle{0.0};
};
// Parameter is the fraction of the transition length, from the straight (0) to the circular curve (1)
local_point transition_point(transition_shape const Shape, double const Length, double const Radius, double const Parameter)
{
if (Length <= 0.0 || Radius <= 0.0)
@@ -81,7 +78,6 @@ local_point transition_point(transition_shape const Shape, double const Length,
auto const x{Length * Parameter};
return {x, x * x * x / (6.0 * Radius * Length), std::atan(x * x / (2.0 * Radius * Length))};
}
// clothoid, tangent angle grows with the square of the travelled distance
auto const distance{Length * Parameter};
auto const angle = [&](double const S) { return S * S / (2.0 * Radius * Length); };
int const steps{std::max(8, static_cast<int>(std::ceil(distance / 0.5)) * 2)};
@@ -97,7 +93,6 @@ local_point transition_point(transition_shape const Shape, double const Length,
return {x * h / 3.0, y * h / 3.0, angle(distance)};
}
// plan position and direction of travel at given distance from the start of the element
void element_point(element const &Element, transition_shape const Shape, double const Distance, glm::dvec2 &Position, glm::dvec2 &Direction)
{
switch (Element.kind)
@@ -147,7 +142,6 @@ element const &element_at(result const &Result, double const Chainage)
return lookup == Result.elements.begin() ? Result.elements.front() : *std::prev(lookup);
}
// polar form of a cubic Bezier curve; control points of the part between parameters a and b are f(a,a,a), f(a,a,b), f(a,b,b), f(b,b,b)
glm::dvec2 blossom(std::array<glm::dvec2, 4> const &Points, double const A, double const B, double const C)
{
glm::dvec2 r[3], s[2];
@@ -158,7 +152,6 @@ glm::dvec2 blossom(std::array<glm::dvec2, 4> const &Points, double const A, doub
return glm::mix(s[0], s[1], C);
}
// number of cubic pieces needed to represent the element accurately
std::size_t element_pieces(element const &Element, transition_shape const Shape)
{
if (Element.length < 1e-3)
@@ -166,7 +159,6 @@ std::size_t element_pieces(element const &Element, transition_shape const Shape)
switch (Element.kind)
{
case element_kind::arc:
// cubic approximation of a 10 degree arc deviates from the circle by less than 1e-7 of its radius
return static_cast<std::size_t>(std::max(1.0, std::ceil(Element.length / Element.radius / (10.0 * kPi / 180.0) - 1e-9)));
case element_kind::transition_in:
case element_kind::transition_out:
@@ -176,7 +168,7 @@ std::size_t element_pieces(element const &Element, transition_shape const Shape)
}
}
} // namespace
}
double unbalanced_acceleration(double const Speed, double const Radius, double const Cant, limits const &Limits)
{
@@ -190,11 +182,9 @@ double transition_length(double const Speed, double const Radius, double const C
double length{0.0};
if (Cant > 0.0)
{
// rate of cant change in time, and the inclination of the cant ramp
length = std::max(length, Speed * Cant / (3.6 * Limits.cant_rate));
length = std::max(length, Cant / 1000.0 * Limits.ramp_factor * Speed);
}
// rate of change of the unbalanced acceleration
auto const unbalanced{std::max(0.0, unbalanced_acceleration(Speed, Radius, Cant, Limits))};
length = std::max(length, unbalanced * Speed / (3.6 * Limits.jerk));
return length > 0.0 ? ceil_to(length, 5.0) : 0.0;
@@ -203,7 +193,7 @@ double transition_length(double const Speed, double const Radius, double const C
recommendation recommend(double const Speed, double const Radius, limits const &Limits)
{
recommendation result;
auto const factor{Limits.gauge / (kGravity * 12.96)}; // ~11.8 for standard gauge
auto const factor{Limits.gauge / (kGravity * 12.96)};
auto const allowance{Limits.gauge * Limits.unbalanced / kGravity};
result.radius_min = factor * Speed * Speed / (Limits.cant_max + allowance);
result.vertical_radius = std::max(Limits.vertical_min, Limits.vertical_factor * Speed * Speed);
@@ -211,7 +201,6 @@ recommendation recommend(double const Speed, double const Radius, limits const &
return result;
result.cant_equilibrium = factor * Speed * Speed / Radius;
result.cant_min = std::max(0.0, result.cant_equilibrium - allowance);
// keep a part of the permissible unbalanced acceleration in reserve, but never go below the required minimum
result.cant = std::min(Limits.cant_max, ceil_to(std::max(result.cant_min, result.cant_equilibrium * 2.0 / 3.0), 5.0));
result.transition = transition_length(Speed, Radius, result.cant, Limits);
return result;
@@ -229,7 +218,6 @@ result compute(design const &Design)
auto const enddirection{glm::normalize(Design.end_direction)};
auto const count{Design.vertices.size()};
// resolve vertex positions; the first and the last vertex lie on the tangents of the fixed ends
r.vertices.resize(count);
if (count == 0)
{
@@ -307,7 +295,6 @@ result compute(design const &Design)
auto const lead{glm::dot(position - cursor, directionin)};
if (deflection < 1e-7)
{
// tangents are collinear, there's no curve to fit
if (lead < -1e-4)
r.errors.emplace_back(format("Vertex %zu lies inside the curve of the previous vertex", k + 1));
push_straight(cursor, directionin, lead);
@@ -332,13 +319,11 @@ result compute(design const &Design)
auto const lengthout{std::max(0.0, vertex.transition_out)};
auto const endin{transition_point(Design.shape, lengthin, radius, 1.0)};
auto const endout{transition_point(Design.shape, lengthout, radius, 1.0)};
// shift of the circular curve towards its centre, and position of the shifted curve start along the tangent
auto const shiftin{endin.y - radius * (1.0 - std::cos(endin.angle))};
auto const shiftout{endout.y - radius * (1.0 - std::cos(endout.angle))};
auto const middlein{endin.x - radius * std::sin(endin.angle)};
auto const middleout{endout.x - radius * std::sin(endout.angle)};
// the centre lies at distances R + shift from both tangents
auto const normalin{perpendicular(directionin) * static_cast<double>(side)};
auto const normalout{perpendicular(directionout) * static_cast<double>(side)};
auto const a{radius + shiftin};
@@ -420,7 +405,6 @@ result compute(design const &Design)
report.recommended = recommend(Design.speed, radius, Design.norms);
r.curves.push_back(report);
// verification against the limits
auto const &norms{Design.norms};
if (radius < report.recommended.radius_min - 1e-6)
r.warnings.emplace_back(format("Vertex %zu: R %.0f m is below the minimum %.0f m for %.0f km/h", k + 1, radius, report.recommended.radius_min, Design.speed));
@@ -441,7 +425,6 @@ result compute(design const &Design)
push_straight(cursor, lastdirection, tail);
r.length = chainage;
// straights between curves, measured as travel time
if (Design.norms.tangent_min_time > 0.0)
{
auto const minimum{Design.norms.tangent_min_time * Design.speed / 3.6};
@@ -453,7 +436,6 @@ result compute(design const &Design)
}
}
// vertical profile: grades between the fixed ends and the vertex elevations, joined with vertical curves
r.vertex_chainages = vertexchainage;
r.profile.push_back({0.0, Design.start.y, 0.0, 0.0});
for (std::size_t k = 0; k < count; ++k)
@@ -463,7 +445,7 @@ result compute(design const &Design)
r.vertex_elevations.push_back(elevation);
auto const radius{vertex.vertical_radius > 0.0 ? vertex.vertical_radius : std::max(Design.norms.vertical_min, Design.norms.vertical_factor * Design.speed * Design.speed)};
if (vertexchainage[k] <= r.profile.back().chainage + 1e-3)
continue; // vertex without its own place in the profile
continue;
r.profile.push_back({vertexchainage[k], elevation, radius, 0.0});
}
r.profile.push_back({r.length, Design.end.y, 0.0, 0.0});
@@ -488,7 +470,6 @@ result compute(design const &Design)
if (std::abs(last - Design.end_grade) > 0.0005)
r.warnings.emplace_back(format("Grade break at the end: %.1f per mille against %.1f of the adjoining track", last * 1000.0, Design.end_grade * 1000.0));
}
// curvature continuity with the adjoining tracks; the designed fragment starts and ends on a tangent
if (Design.start_radius > 0.0 && Design.start_radius < 10000.0)
r.warnings.emplace_back(format("The adjoining track at the start is curved (R %.0f m), the curvature changes abruptly", Design.start_radius));
if (Design.end_radius > 0.0 && Design.end_radius < 10000.0)
@@ -615,7 +596,6 @@ std::vector<segment_data> pieces(result const &Result, design const &Design, std
for (std::size_t j = 0; j < count; ++j)
spans.push_back({i, e.length * j / count, e.length * (j + 1) / count});
}
// use up the requested number of pieces by dividing the longest ones
while (false == spans.empty() && spans.size() < Count)
{
auto longest = std::max_element(spans.begin(), spans.end(), [](span const &A, span const &B) { return A.to - A.from < B.to - B.from; });
@@ -651,7 +631,6 @@ std::vector<segment_data> pieces(result const &Result, design const &Design, std
{
if (Design.shape == transition_shape::cubic_parabola)
{
// exact: part of the cubic which describes the whole transition
std::array<glm::dvec2, 4> const local{glm::dvec2{0.0, 0.0}, glm::dvec2{e.transition / 3.0, 0.0}, glm::dvec2{e.transition * 2.0 / 3.0, 0.0}, glm::dvec2{e.transition, e.transition * e.transition / (6.0 * e.radius)}};
auto const a{e.kind == element_kind::transition_in ? piece.from / e.transition : 1.0 - piece.from / e.transition};
auto const b{e.kind == element_kind::transition_in ? piece.to / e.transition : 1.0 - piece.to / e.transition};
@@ -670,7 +649,6 @@ std::vector<segment_data> pieces(result const &Result, design const &Design, std
break;
}
}
// heights follow the profile, with end slopes matching its grade
auto const s0{e.chainage + piece.from};
auto const s3{e.chainage + piece.to};
auto const y0{elevation(Result, s0)};
@@ -693,4 +671,4 @@ std::vector<segment_data> pieces(result const &Result, design const &Design, std
return result;
}
} // namespace alignment
}

View File

@@ -4,7 +4,7 @@ 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://mozilla.org/MPL/2.0/.
http:
*/
#pragma once
@@ -15,80 +15,68 @@ http://mozilla.org/MPL/2.0/.
#include "world/Segment.h"
// Railway alignment design: horizontal alignment defined by a polygon of tangents, with a circular curve
// and transition curves fitted at each intersection point (vertex), and a vertical profile made of grades
// joined with parabolic vertical curves.
//
// Plan coordinates are (x, z) of the scene; heights are scene y. Design values follow the usual railway
// conventions: speed in km/h, cant in mm, radii and lengths in metres. All the limits are configurable.
namespace alignment
{
enum class transition_shape
{
cubic_parabola, // y = x^3 / (6RL), represented exactly by a single cubic Bezier
clothoid // curvature linear along the length, approximated by cubic pieces
cubic_parabola,
clothoid
};
// permissible values used to recommend and verify the design
struct limits
{
double gauge{1500.0}; // distance between rail axes, mm
double unbalanced{0.6}; // permissible unbalanced lateral acceleration, m/s2
double cant_max{150.0}; // maximum cant, mm
double cant_rate{35.0}; // permissible rate of cant change along transition, mm/s
double ramp_factor{10.0}; // cant ramp inclination limited to 1 / (factor * V)
double jerk{0.3}; // permissible rate of change of unbalanced acceleration, m/s3
double vertical_factor{0.4}; // minimum vertical curve radius = factor * V^2
double vertical_min{2000.0}; // absolute minimum vertical curve radius, m
double tangent_min_time{0.0}; // minimum length of a straight between curves expressed as travel time, s (0: not checked)
double gauge{1500.0};
double unbalanced{0.6};
double cant_max{150.0};
double cant_rate{35.0};
double ramp_factor{10.0};
double jerk{0.3};
double vertical_factor{0.4};
double vertical_min{2000.0};
double tangent_min_time{0.0};
};
// intersection point of two tangents of the horizontal alignment, with the curve fitted there
struct vertex
{
glm::dvec2 position{0.0}; // plan position of an unconstrained vertex
double offset{100.0}; // first/last vertex: distance from the fixed end along its tangent
glm::dvec2 position{0.0};
double offset{100.0};
double radius{1000.0};
double transition_in{0.0};
double transition_out{0.0};
double cant{0.0}; // mm
bool auto_elevation{true}; // elevation interpolated between the fixed ends
double elevation{0.0}; // height of the profile at the vertex
double vertical_radius{0.0}; // 0: minimum radius from the limits
double cant{0.0};
bool auto_elevation{true};
double elevation{0.0};
double vertical_radius{0.0};
};
struct design
{
// fixed ends of the designed fragment, with plan directions pointing along the route
glm::dvec3 start{0.0};
glm::dvec3 end{0.0};
glm::dvec2 start_direction{0.0, 1.0};
glm::dvec2 end_direction{0.0, 1.0};
double start_grade{0.0}; // grade of the adjoining track, for continuity check
double start_grade{0.0};
double end_grade{0.0};
double start_radius{0.0}; // plan radius of the adjoining track at the joint, 0 for straight
double start_radius{0.0};
double end_radius{0.0};
std::vector<vertex> vertices;
transition_shape shape{transition_shape::cubic_parabola};
double speed{100.0}; // design speed, km/h
double speed{100.0};
limits norms;
};
// values derived from the design speed for a curve of given radius
struct recommendation
{
double cant_equilibrium{0.0}; // cant compensating the lateral acceleration fully, mm
double cant_min{0.0}; // smallest cant keeping the unbalanced acceleration within the limit, mm
double cant{0.0}; // proposed cant, mm
double radius_min{0.0}; // smallest radius for the design speed with maximum cant, m
double transition{0.0}; // minimum transition length for the proposed cant, m
double vertical_radius{0.0}; // minimum vertical curve radius, m
double cant_equilibrium{0.0};
double cant_min{0.0};
double cant{0.0};
double radius_min{0.0};
double transition{0.0};
double vertical_radius{0.0};
};
recommendation recommend(double const Speed, double const Radius, limits const &Limits);
// minimum transition length for given speed, cant and radius
double transition_length(double const Speed, double const Radius, double const Cant, limits const &Limits);
// unbalanced lateral acceleration, m/s2
double unbalanced_acceleration(double const Speed, double const Radius, double const Cant, limits const &Limits);
enum class element_kind
@@ -99,36 +87,31 @@ enum class element_kind
transition_out
};
// single geometric element of the computed alignment
struct element
{
element_kind kind{element_kind::straight};
int vertex{-1}; // vertex the curve element belongs to
double chainage{0.0}; // distance from the start of the fragment to the start of the element
int vertex{-1};
double chainage{0.0};
double length{0.0};
double radius{0.0}; // curve radius (also the final radius of a transition)
double transition{0.0}; // transition length
int turn{0}; // +1/-1 direction of the curve, 0 for straights
double cant{0.0}; // full cant of the curve, mm
// geometric frame: straight - start point and direction; arc - centre, radius vector to its start point
// and direction at the start; transition_in - its start point (TS) with the tangent direction and inner normal;
// transition_out - its end point (ST) with the direction pointing backwards and inner normal
double radius{0.0};
double transition{0.0};
int turn{0};
double cant{0.0};
glm::dvec2 origin{0.0};
glm::dvec2 direction{0.0, 1.0};
glm::dvec2 normal{0.0};
};
// verification of a single curve against the limits
struct curve_report
{
int vertex{-1};
double deflection{0.0}; // angle between the tangents, radians
double deflection{0.0};
double arc_length{0.0};
double tangent_in{0.0}; // distance from the vertex to the start of the curve
double tangent_out{0.0}; // distance from the vertex to the end of the curve
double unbalanced{0.0}; // m/s2
double cant_rate_in{0.0}, cant_rate_out{0.0}; // mm/s
double jerk_in{0.0}, jerk_out{0.0}; // m/s3
double tangent_in{0.0};
double tangent_out{0.0};
double unbalanced{0.0};
double cant_rate_in{0.0}, cant_rate_out{0.0};
double jerk_in{0.0}, jerk_out{0.0};
recommendation recommended;
};
@@ -136,8 +119,8 @@ struct profile_point
{
double chainage{0.0};
double elevation{0.0};
double radius{0.0}; // vertical curve radius at the grade break
double tangent{0.0}; // vertical curve tangent length
double radius{0.0};
double tangent{0.0};
};
struct result
@@ -147,9 +130,9 @@ struct result
double gauge{1500.0};
std::vector<std::string> errors;
std::vector<std::string> warnings;
std::vector<glm::dvec2> vertices; // resolved plan positions of the vertices
std::vector<double> vertex_chainages; // chainage of the middle of each vertex curve
std::vector<double> vertex_elevations; // profile elevation at each vertex
std::vector<glm::dvec2> vertices;
std::vector<double> vertex_chainages;
std::vector<double> vertex_elevations;
std::vector<element> elements;
std::vector<curve_report> curves;
std::vector<profile_point> profile;
@@ -157,12 +140,9 @@ struct result
};
result compute(design const &Design);
// intersection of the tangents of the fixed ends, if it lies ahead of both ends
bool tangent_intersection(design const &Design, glm::dvec2 &Point);
// checks whether the fixed ends lie on a common straight line
bool collinear(design const &Design);
// position, direction of travel (unit, 3d), cant and curvature direction at specified chainage
struct station
{
glm::dvec3 position{0.0};
@@ -171,14 +151,10 @@ struct station
int turn{0};
};
station evaluate(result const &Result, double const Chainage);
// profile height and grade at specified chainage
double elevation(result const &Result, double const Chainage);
double grade(result const &Result, double const Chainage);
// smallest number of cubic pieces representing the alignment with the required accuracy
std::size_t minimum_pieces(result const &Result, design const &Design);
// splits the alignment into at least Count cubic pieces (the longest pieces are divided further to reach the count),
// in the order of the route, as path source data with absolute end points and relative control vectors
std::vector<segment_data> pieces(result const &Result, design const &Design, std::size_t const Count);
} // namespace alignment
}

View File

@@ -4,7 +4,7 @@ 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://mozilla.org/MPL/2.0/.
http:
*/
#include "stdafx.h"
@@ -26,7 +26,6 @@ http://mozilla.org/MPL/2.0/.
namespace
{
// end points closer than this are treated as the same point
double const kSamePoint{0.05};
segment_data::point point_index(editor_track::point_kind const Kind)
@@ -56,7 +55,6 @@ void add_unique(std::vector<TTrack *> &Tracks, TTrack *Track)
Tracks.emplace_back(Track);
}
// first derivative of the path curve at its start or end, in the direction from the start to the end
glm::dvec3 path_tangent(segment_data const &Path, bool const Atend)
{
auto const &start{Path.points[segment_data::point::start]};
@@ -68,7 +66,6 @@ glm::dvec3 path_tangent(segment_data const &Path, bool const Atend)
return control2 != glm::dvec3{} ? -control2 : end - (start + control1);
}
// plan radius of the path curve at specified parameter, 0 if it's straight there
double path_radius(segment_data const &Path, double const T)
{
auto const &p0{Path.points[segment_data::point::start]};
@@ -87,7 +84,6 @@ double path_radius(segment_data const &Path, double const T)
return curvature > 1e-7 ? 1.0 / curvature : 0.0;
}
// the same path run in the opposite direction
segment_data reversed(segment_data const &Path)
{
segment_data result{Path};
@@ -95,13 +91,10 @@ segment_data reversed(segment_data const &Path)
result.points[segment_data::point::end] = Path.points[segment_data::point::start];
result.points[segment_data::point::control1] = Path.points[segment_data::point::control2];
result.points[segment_data::point::control2] = Path.points[segment_data::point::control1];
// roll follows the direction of the curve, which flips along with the direction of travel
result.rolls = {-Path.rolls[1], -Path.rolls[0]};
return result;
}
// direction of travel and plan radius at the joint of a chain with an adjoining path.
// Leaving: the chain continues into the adjoining path (its end), otherwise it comes from it (its start)
bool adjoining_direction(TTrack const *Neighbour, glm::dvec3 const &Joint, bool const Leaving, glm::dvec3 &Direction, double &Radius)
{
if (Neighbour == nullptr)
@@ -113,8 +106,6 @@ bool adjoining_direction(TTrack const *Neighbour, glm::dvec3 const &Joint, bool
auto const &point{path.points[atend ? segment_data::point::end : segment_data::point::start]};
if (glm::distance(point, Joint) > kSamePoint)
continue;
// the tangent runs from the start of the neighbour to its end; it's the direction of travel when
// entering the neighbour at its start, or arriving from it through its end
auto const tangent{glm::normalize(path_tangent(path, atend))};
Direction = (Leaving != atend) ? tangent : -tangent;
Radius = path_radius(path, atend ? 1.0 : 0.0);
@@ -124,7 +115,7 @@ bool adjoining_direction(TTrack const *Neighbour, glm::dvec3 const &Joint, bool
return false;
}
} // namespace
}
editor_track::state editor_track::capture(TTrack const &Track)
{
@@ -175,7 +166,7 @@ void editor_track::apply(TTrack &Track, state const &State)
bool editor_track::is_supported(TTrack const &Track)
{
if ((Track.iCategoryFlag & 0x80) != 0)
return false; // auto-generated helper path
return false;
if (Track.eType == tt_Normal)
return Track.Segment != nullptr;
if (Track.eType == tt_Switch)
@@ -209,7 +200,6 @@ void editor_track::commit(std::vector<TTrack *> const &Tracks)
if (Tracks.empty())
return;
// paths whose render geometry depends on the modified ones
std::vector<TTrack *> affected;
for (auto *track : Tracks)
{
@@ -217,27 +207,23 @@ void editor_track::commit(std::vector<TTrack *> const &Tracks)
for (auto *neighbour : neighbours(*track))
add_unique(affected, neighbour);
}
// take the paths out of the scene while their ends still match the old segments...
for (auto *track : Tracks)
{
disconnect(*track);
simulation::Region->erase_and_unregister(track);
}
// ...rebuild the segments and put the paths back in their new place...
for (auto *track : Tracks)
{
track->init_segments(false);
track->update_location();
track->m_area.radius = -1.f; // forces radius recalculation
track->m_area.radius = -1.f;
simulation::Region->insert_and_register(track);
}
// ...connect them with whatever they touch now...
for (auto *track : Tracks)
join(*track);
for (auto *track : Tracks)
for (auto *neighbour : neighbours(*track))
add_unique(affected, neighbour);
// ...and update the visuals
for (auto *track : affected)
update_transition(*track);
for (auto *track : affected)
@@ -249,7 +235,6 @@ void editor_track::commit(std::vector<TTrack *> const &Tracks)
void editor_track::commit_parameters(TTrack &Track)
{
Track.fTrackWidth2 = Track.fTrackWidth;
// segments are re-created from unchanged data, but road texture proportions and roll fix depend on parameters
Track.init_segments(false);
std::vector<TTrack *> affected{&Track};
@@ -302,8 +287,6 @@ void editor_track::move_point(TTrack &Track, point_ref const &Point, glm::dvec3
auto &point{path.points[point_index(Point.kind)]};
auto const oldposition{point};
point = Position;
// control vectors are relative to their end points, so they follow them automatically.
// other paths of the same piece sharing this point (the common start of a switch) follow as well
for (auto &otherpath : Track.m_paths)
{
if (&otherpath == &path)
@@ -406,7 +389,6 @@ editor_track::snap_target editor_track::find_snap_target(TTrack const &Track, gl
auto const excluded = [&](TTrack const *Other) { return std::find(Exclude.begin(), Exclude.end(), Other) != Exclude.end(); };
auto const category{Track.iCategoryFlag & 15};
// NOTE: copy, the region returns its scratchpad
auto const sections{simulation::Region->sections(Position, static_cast<float>(Radius))};
for (auto *section : sections)
{
@@ -425,7 +407,6 @@ editor_track::snap_target editor_track::find_snap_target(TTrack const &Track, gl
auto const distance{glm::distance(point, Position)};
if (distance > Radius || distance >= result.distance)
continue;
// skip ends already connected with something which stays in place
auto const connections{connected_points(*other, point)};
if (std::any_of(connections.begin(), connections.end(), [&](auto const &Connection) { return false == excluded(Connection.first); }))
continue;
@@ -466,7 +447,6 @@ void editor_track::snap_point(TTrack &Track, point_ref const &Point, snap_target
if (false == Aligntangent)
return;
// the path continues the target, so it leaves the shared point in the opposite direction
auto &path{Track.m_paths[Point.path]};
auto const &start{path.points[segment_data::point::start]};
auto const &end{path.points[segment_data::point::end]};
@@ -515,7 +495,6 @@ void editor_track::velocity(TTrack &Track, double const Velocity)
{
if (Track.SwitchExtension && Track.eType == tt_Switch)
{
// same rules as TTrack::Switch()
Track.SwitchExtension->fVelocity = static_cast<float>(Velocity);
if (Velocity <= -2.0)
Track.fVelocity = Track.SwitchExtension->CurrentIndex ? -Velocity : -1.0;
@@ -594,7 +573,6 @@ void editor_track::join(TTrack &Track)
}
case tt_Switch:
{
// like during scenery load, switches are connected from the side of the regular paths
for (int i = 0; i < 2; ++i)
{
auto const &segment{Track.SwitchExtension->Segments[i]};
@@ -638,7 +616,6 @@ void editor_track::connect(TTrack &Track, bool const Prevside, TTrack *Other, in
{
if (false == has_switch_paths(*Other))
break;
// same connections as in path_table::InitTracks(), but made without moving the switch blades
auto const path{(Endpointid - 2) / 2};
auto const atend{((Endpointid - 2) % 2) != 0};
bind_switch_path(*Other, path);
@@ -667,7 +644,6 @@ void editor_track::connect(TTrack &Track, bool const Prevside, TTrack *Other, in
void editor_track::update_transition(TTrack &Track)
{
// same rule as TTrack::ConnectNextPrev(): transition is drawn by the path whose end 2 meets start of a different path
Track.iTrapezoid &= ~2;
auto const *next{Track.trNext};
if (next == nullptr || Track.iNextDirection != 0)
@@ -692,7 +668,6 @@ void editor_track::bind_switch_path(TTrack &Switch, int const Path)
void editor_track::store_switch_path(TTrack &Switch, int const Path)
{
// same as TTrack::SetConnections(), without switching back to the base position
auto &extension{*Switch.SwitchExtension};
auto const path{Path & 1};
extension.pNexts[path] = Switch.trNext;
@@ -701,7 +676,6 @@ void editor_track::store_switch_path(TTrack &Switch, int const Path)
extension.iPrevDirection[path] = Switch.iPrevDirection;
if (Switch.eType == tt_Switch)
{
// switch paths share their start point, so they share the path connected there as well
extension.pPrevs[path ^ 1] = Switch.trPrev;
extension.iPrevDirection[path ^ 1] = Switch.iPrevDirection;
}
@@ -727,7 +701,6 @@ bool editor_track::find_chain(TTrack *From, TTrack *To, chain &Chain, std::strin
if (From != To)
{
bool found{false};
// try to reach the last path leaving the first one through its end, then through its start
for (auto const throughend : {true, false})
{
Chain.tracks = {From};
@@ -780,7 +753,6 @@ bool editor_track::find_chain(TTrack *From, TTrack *To, chain &Chain, std::strin
Chain.radius = radius;
}
// fixed ends, with directions taken from the adjoining paths when there are any
auto const &first{From->m_paths.front()};
auto const firstforward{Chain.forward.front()};
Chain.start = first.points[firstforward ? segment_data::point::start : segment_data::point::end];
@@ -806,7 +778,6 @@ std::vector<TTrack *> editor_track::relay(chain const &Chain, std::vector<segmen
if (count == 0 || Pieces.size() < count)
return created;
// existing paths are spread evenly over the pieces, added paths fill the gaps between them
std::vector<TTrack *> tracks;
std::size_t previous{count};
for (std::size_t i = 0; i < Pieces.size(); ++i)
@@ -841,7 +812,7 @@ TTrack *editor_track::clone(TTrack const &Template)
cParser parser(text.str(), cParser::buffer_TEXT);
parser.getTokens();
std::string token;
parser >> token; // node type, consumed before the path is loaded
parser >> token;
scene::node_data data;
data.type = "track";
@@ -854,7 +825,6 @@ TTrack *editor_track::clone(TTrack const &Template)
track->m_rangesquaredmin = Template.m_rangesquaredmin;
track->m_rangesquaredmax = Template.m_rangesquaredmax;
track->Load(&parser, glm::dvec3{});
// events stay with the original path only
for (auto *events : {&track->m_events0, &track->m_events1, &track->m_events2, &track->m_events0all, &track->m_events1all, &track->m_events2all})
events->clear();
track->m_events = false;
@@ -885,6 +855,5 @@ void editor_track::revive(TTrack &Track)
void editor_track::rebuild_geometry(TTrack &Track)
{
auto &section{simulation::Region->section(Track.location())};
// sections which weren't displayed yet create geometry of their content on their own
Track.rebuild_geometry(section.m_geometrycreated ? section.m_geometrybank : gfx::geometrybank_handle{});
}

View File

@@ -4,7 +4,7 @@ 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://mozilla.org/MPL/2.0/.
http:
*/
#pragma once
@@ -19,25 +19,18 @@ http://mozilla.org/MPL/2.0/.
class TTrack;
// Editing of paths (tracks, roads, rivers) loaded from the scenery.
//
// The source of truth for path geometry is TTrack::m_paths (the same data the scenery export writes).
// Edits modify m_paths and then commit() rebuilds everything derived from it: the segments, placement
// in the scene cells, connections with neighbouring paths and the render geometry.
class editor_track
{
public:
// identifies an editable point of a path
enum class point_kind { start, control1, control2, end };
struct point_ref
{
int path{-1}; // index into TTrack::m_paths
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; }
};
// snapshot of the editable state of a path, used by the undo history
struct state
{
std::vector<segment_data> paths;
@@ -58,85 +51,57 @@ class editor_track
float texslope{0.9f};
};
static state capture(TTrack const &Track);
// restores provided state; follow with commit() to rebuild the path
static void apply(TTrack &Track, state const &State);
// checks whether geometry of the path can be modified. returns: true if it can, otherwise false and the reason
static bool can_edit_geometry(TTrack const &Track, std::string &Reason);
// checks whether the path type is supported by the editor at all
static bool is_supported(TTrack const &Track);
// rebuilds provided paths after modification of their source data (m_paths): segments, placement in the region,
// connections with neighbours and render geometry of the paths and all their old and new neighbours
static void commit(std::vector<TTrack *> const &Tracks);
// rebuilds the path after change of parameters which don't affect its course (width, textures, etc)
static void commit_parameters(TTrack &Track);
// world position of specified point. for control points it's the absolute position of the handle;
// a zero (straight) control vector is presented at 1/3 of the chord so it can be grabbed
static glm::dvec3 point_position(TTrack const &Track, point_ref const &Point);
// moves specified point to provided world position. moving an end point carries its control vector along.
// switches keep the start points of both their paths together
static void move_point(TTrack &Track, point_ref const &Point, glm::dvec3 const &Position);
// moves the whole path by provided offset
static void translate(TTrack &Track, glm::dvec3 const &Offset);
// rotates the whole path around vertical axis going through provided pivot point
static void rotate_y(TTrack &Track, glm::dvec3 const &Pivot, double const Angle);
// pivot used for rotation of the whole path
static glm::dvec3 pivot(TTrack const &Track);
// lists paths currently connected to provided path
static std::vector<TTrack *> neighbours(TTrack const &Track);
// lists end points of other paths connected to the end point located at provided position,
// as pairs of the neighbour and its matching point
static std::vector<std::pair<TTrack *, point_ref>> connected_points(TTrack const &Track, glm::dvec3 const &Position);
// checks whether end point located at provided position is connected to another path
static bool is_connected(TTrack const &Track, point_ref const &Point);
// candidate to connect a dragged end point with
struct snap_target
{
TTrack *track{nullptr};
point_ref point;
glm::dvec3 position{0.0};
glm::dvec3 direction{0.0}; // direction from the point into the target path
glm::dvec3 direction{0.0};
double distance{0.0};
};
// finds the nearest free end point of another path of the same category as provided path, within specified radius.
// paths listed in Exclude are skipped, and connections to them don't count as occupying an end point
static snap_target find_snap_target(TTrack const &Track, glm::dvec3 const &Position, double const Radius, std::vector<TTrack const *> const &Exclude);
// places the point at the target and optionally aligns the control vector with the target path
static void snap_point(TTrack &Track, point_ref const &Point, snap_target const &Target, bool const Aligntangent);
// sequence of regular paths connected end to end
struct chain
{
std::vector<TTrack *> tracks;
std::vector<bool> forward; // the path runs in the direction of the chain (its start is closer to the chain start)
glm::dvec3 start{0.0}; // outer end of the first path
glm::dvec3 end{0.0}; // outer end of the last path
glm::dvec3 start_direction{0.0}; // direction of travel along the chain at its ends, from the adjoining paths if there are any
std::vector<bool> 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}; // plan radius of the adjoining paths at the joints, 0 for straight
double start_radius{0.0};
double end_radius{0.0};
double length{0.0};
double velocity{-1.0}; // highest speed limit of the paths in the chain
double radius{0.0}; // smallest radius of the paths in the chain, 0 if all are straight
double velocity{-1.0};
double radius{0.0};
};
// collects regular paths connected between provided paths, both included. returns: true on success
static bool find_chain(TTrack *From, TTrack *To, chain &Chain, std::string &Error);
// lays the paths of the chain along provided pieces, in the order of the chain. if there are more pieces than paths,
// additional paths are made as copies of their neighbours. returns: the added paths
static std::vector<TTrack *> relay(chain const &Chain, std::vector<segment_data> const &Pieces);
// takes a path added by the editor out of the scene, or puts it back
static void retire(TTrack &Track);
static void revive(TTrack &Track);
// names of the materials used by the path; "none" for missing ones
static std::string material_name(material_handle const Material);
static material_handle fetch_material(std::string const &Name);
// height of the trackbed edge as written in the scenery (without roll fix adjustment)
static float texture_height(TTrack const &Track);
static void texture_height(TTrack &Track, float const Height);
static double velocity(TTrack const &Track);
@@ -144,19 +109,12 @@ class editor_track
static void damage(TTrack &Track, int const Damage);
private:
// unlinks the path from all its neighbours
static void disconnect(TTrack &Track);
// links the path with paths whose free end points match its own free end points
static void join(TTrack &Track);
// links specified end of a regular path with matching end of another path
static void connect(TTrack &Track, bool const Prevside, TTrack *Other, int const Endpointid);
// recalculates the trackbed transition flag
static void update_transition(TTrack &Track);
// sets active connections of a switch to specified path, without moving the blades
static void bind_switch_path(TTrack &Switch, int const Path);
static void store_switch_path(TTrack &Switch, int const Path);
// rebuilds render geometry of the path in the bank of the section it's placed in
static void rebuild_geometry(TTrack &Track);
// makes a new path with the parameters of provided path (without its events) and registers it in the path table
static TTrack *clone(TTrack const &Template);
};