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

feat(editor): the work of the editor sessions not committed yet, as it stands: a curve traced on from the end of the diverging track of a switch goes on at its radius with no transition in, or from a straight right at the switch (alignment start_held, fit_going_on), the plan taking an end adjoining R 10 km or more for a straight; the switches and slips laid into curves, slid along them and laid alone on one straight (frame_path, switch slide); the route plan reading curves going on from the tracks adjoining, the kept designs taken or forgotten; the orthophoto and terrain model settings, downloads and layers, the tracks onto the terrain; the curvature of a path in the plan (bezier::plan_curvature); the Polish texts of all of it. Not built as committed

This commit is contained in:
maj00r
2026-10-29 22:19:06 +01:00
parent 2eda26b94c
commit 80379b991c
49 changed files with 4718 additions and 1114 deletions

View File

@@ -89,11 +89,7 @@ double path_radius(segment_data const &Path, double const T)
{
if (Path.points[segment_data::point::control1] == glm::dvec3{} && Path.points[segment_data::point::control2] == glm::dvec3{})
return 0.0;
bezier const curve{Path};
auto const first{curve.first(T)};
auto const second{curve.second(T)};
auto const speed{std::hypot(first.x, first.z)};
auto const curvature{std::abs(first.x * second.z - first.z * second.x) / (speed * speed * speed)};
auto const curvature{std::abs(bezier{Path}.plan_curvature(T))};
return curvature > 1e-7 ? 1.0 / curvature : 0.0;
}
@@ -108,25 +104,118 @@ 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;
auto const tangent{glm::normalize(path_tangent(path, atend))};
// measured on the piece at the end where the path has them: the path only approximates them, its curvature off at its ends,
// R 300.9 at the end of the diverging track of a switch of R 300
auto const &end{path.pieces.empty() ? path : atend ? path.pieces.back() : path.pieces.front()};
auto const tangent{glm::normalize(path_tangent(end, atend))};
auto const along{Leaving != atend};
Direction = along ? tangent : -tangent;
Radius = path_radius(path, atend ? 1.0 : 0.0);
Radius = path_radius(end, atend ? 1.0 : 0.0);
// the side it turns to as the chain runs, the path gone against its points turning the other way
Turn = 0;
if (Radius > 0.0)
{
bezier const curve{path};
auto const first{curve.first(atend ? 1.0 : 0.0)};
auto const second{curve.second(atend ? 1.0 : 0.0)};
Turn = (first.x * second.z - first.z * second.x > 0.0) == along ? 1 : -1;
}
Turn = (bezier{end}.plan_curvature(atend ? 1.0 : 0.0) > 0.0) == along ? 1 : -1;
return true;
}
}
return false;
}
// the element of a stored design a regular track lies along from end to end: its curvature as designed rather than as the path
// approximates it, an arc of its radius however short it is, or a transition. None where the track lies along no design, or across
// the joint of two of its elements
std::optional<alignment::element> designed_element(TTrack const &Track)
{
if (Track.eType != tt_Normal || Track.m_paths.empty())
return std::nullopt;
auto const found{alignment_store::on_track(Track)};
if (false == found.has_value())
return std::nullopt;
bezier const curve{Track.m_paths.front()};
for (auto const t : {0.1, 0.9})
if (auto const at{alignment_store::on_record(found->record, curve.point(t), 0.05)}; false == at.has_value() || at->element != found->element)
return std::nullopt;
auto const &elements{alignment_store::records()[static_cast<std::size_t>(found->record)].result.elements};
if (found->element < 0 || found->element >= static_cast<int>(elements.size()))
return std::nullopt;
return elements[static_cast<std::size_t>(found->element)];
}
// a stretch of a curve along the run of its paths: its length, its curvature at its start and at its end, unsigned
struct curve_piece
{
double length{0.0};
double from{0.0};
double to{0.0};
};
// the curvature of an element of a design at its start and at its end, unsigned
std::pair<double, double> element_curvatures(alignment::element const &Element)
{
auto const [start, end]{alignment::end_curvatures(Element)};
return {std::abs(start), std::abs(end)};
}
// the pieces of a curve along the run of its paths: of the elements of the stored design a path lies along, of the path itself where
// it lies along none, its curvature as at its middle where it barely changes along it. False where no path lies along a design
bool designed_pieces(std::vector<std::pair<TTrack *, bool>> const &Run, std::vector<curve_piece> &Pieces)
{
Pieces.clear();
auto designed{false};
for (auto const &[track, forward] : Run)
{
auto const &path{track->m_paths.front()};
auto const length{track->eType == tt_Switch ? bezier{path}.plan_length() : track->Length()};
auto const &begin{path.points[forward ? segment_data::point::start : segment_data::point::end]};
auto const &finish{path.points[forward ? segment_data::point::end : segment_data::point::start]};
std::optional<alignment_store::hit> first;
std::optional<alignment_store::hit> last;
if (auto const found{track->eType == tt_Normal ? alignment_store::on_track(*track) : std::nullopt})
{
first = alignment_store::on_record(found->record, begin, 0.05);
last = alignment_store::on_record(found->record, finish, 0.05);
}
if (false == first.has_value() || false == last.has_value() || first->record != last->record || std::abs(last->chainage - first->chainage) < 1e-3)
{
auto const curvature_at = [&](double const T) {
auto const radius{path_radius(path, forward ? T : 1.0 - T)};
return radius > 0.0 ? 1.0 / radius : 0.0;
};
auto const start{curvature_at(0.0)};
auto const end{curvature_at(1.0)};
if (std::abs(start - end) <= 0.1 * std::max(start, end))
Pieces.push_back({length, curvature_at(0.5), curvature_at(0.5)});
else
Pieces.push_back({length, start, end});
continue;
}
designed = true;
auto const &elements{alignment_store::records()[static_cast<std::size_t>(first->record)].result.elements};
auto const low{std::min(first->chainage, last->chainage)};
auto const high{std::max(first->chainage, last->chainage)};
std::vector<curve_piece> own;
for (auto const &element : elements)
{
auto const from{std::max(low, element.chainage)};
auto const to{std::min(high, element.chainage + element.length)};
if (to - from < 1e-6)
continue;
auto const [start, end]{element_curvatures(element)};
auto const at = [&](double const Chainage) { return element.length > 1e-9 ? start + (end - start) * (Chainage - element.chainage) / element.length : start; };
own.push_back({to - from, at(from), at(to)});
}
// the design running against the run
if (last->chainage < first->chainage)
{
std::reverse(own.begin(), own.end());
for (auto &piece : own)
std::swap(piece.from, piece.to);
}
Pieces.insert(Pieces.end(), own.begin(), own.end());
}
return designed;
}
}
editor_track::observer *editor_track::s_observer{nullptr};
@@ -628,21 +717,15 @@ std::vector<std::pair<TTrack *, editor_track::point_ref>> editor_track::connecte
return result;
}
namespace
glm::dvec2 editor_track::end_direction(segment_data const &Path, point_kind const Kind)
{
// direction of the path at the end, into the path
glm::dvec2 end_direction(segment_data const &Path, editor_track::point_kind const Kind)
{
bool const start{Kind == editor_track::point_kind::start};
bool const start{Kind == point_kind::start};
auto const &point{Path.points[start ? segment_data::point::start : segment_data::point::end]};
auto const &control{Path.points[start ? segment_data::point::control1 : segment_data::point::control2]};
auto const direction{plan_of(control != glm::dvec3{} ? control : Path.points[start ? segment_data::point::end : segment_data::point::start] - point)};
return glm::length(direction) > 1e-9 ? glm::normalize(direction) : glm::dvec2{0.0, 1.0};
}
} // namespace
std::vector<editor_track::joint> editor_track::joints(TTrack const &Track)
{
std::vector<joint> result;
@@ -1625,7 +1708,14 @@ std::vector<editor_track::straight> editor_track::find_straights(double const Mi
bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance, double const Gauge, curve &Curve)
{
Curve = {};
auto const curved = [&](TTrack const *Other) { return Other != nullptr && Other->eType == tt_Normal && is_supported(*Other) && false == Other->m_editorremoved && false == is_straight(*Other, Tolerance); };
// a track too short to tell its bend from a straight is a part of the curve where it lies along an arc or a transition of a design
auto const designed_bend = [](TTrack const &Other) {
auto const element{designed_element(Other)};
return element.has_value() && element->kind != alignment::element_kind::straight;
};
auto const curved = [&](TTrack const *Other) {
return Other != nullptr && Other->eType == tt_Normal && is_supported(*Other) && false == Other->m_editorremoved && (false == is_straight(*Other, Tolerance) || designed_bend(*Other));
};
if (false == curved(&Track))
return false;
@@ -1814,6 +1904,48 @@ bool editor_track::analyse_curve(std::vector<std::pair<TTrack *, bool>> const &R
Curve.arcs = arcs;
}
}
// along the stored designs the curve is read from their elements, as it was laid, the paths along none by their own curvature: the
// curve of two lines laid one after another made of the arcs and the transitions of both, however short they are
if (std::vector<curve_piece> pieces; Curve.reversals == 0 && designed_pieces(run, pieces))
{
std::vector<curve::arc_part> designedarcs;
double in{0.0};
double between{0.0};
auto inside{false}; // a straight between two arcs: no curve of one vertex
auto straight{false};
for (auto const &piece : pieces)
{
auto const even{std::abs(piece.from - piece.to) <= 0.02 * std::max(piece.from, piece.to)};
if (even && piece.from < 1e-6)
{
straight = false == designedarcs.empty();
continue;
}
if (false == even)
{
(designedarcs.empty() ? in : between) += piece.length;
continue;
}
inside = inside || straight;
auto const radius{2.0 / (piece.from + piece.to)};
if (false == designedarcs.empty() && between < 1e-6 && std::abs(designedarcs.back().radius - radius) <= 0.02 * radius)
designedarcs.back().turn += piece.length / radius;
else
designedarcs.push_back({radius, piece.length / radius, designedarcs.empty() ? 0.0 : between});
between = 0.0;
}
if (false == designedarcs.empty() && false == inside)
{
Curve.laid = true;
Curve.transition_in = in;
Curve.transition_out = between;
Curve.radius = designedarcs.front().radius;
Curve.compound = designedarcs.size() > 1;
Curve.arcs.clear();
if (Curve.compound)
Curve.arcs = designedarcs;
}
}
if (Curve.reversals == 0)
{
if (auto const designed{alignment_store::vertex_of(Run)})
@@ -2853,6 +2985,8 @@ std::optional<editor_track::switch_template> editor_track::template_of(switch_de
entry.movable = Design.movable;
entry.heel = Design.heel;
entry.bladethrow = Design.bladethrow;
if (false == Design.parts.empty() && Design.parts.back().radius1 <= 0.0 && Design.parts.back().radius2 <= 0.0)
entry.frog_straight = Design.parts.back().length;
entry.local = {shape.paths[0], shape.paths[1]};
entry.local[1].radius = static_cast<float>(shape.radius);
if (shape.paths.size() >= 5)
@@ -3022,7 +3156,7 @@ std::vector<editor_track::switch_template> editor_track::standard_switch_templat
slip.rail = std::string{entry.name}.substr(0, std::string{entry.name}.find('-'));
slip.label = std::string{entry.type} + ' ' + entry.name + format(" a %.3f", entry.half) + (slip.type == "Rkp" ? " single slip" : " double slip") +
(entry.middle < 0.0 ? ", the blades outside of the diamond" : "") +
(slip.type == "Rkp" ? " (click at a crossing of two straights, on the side of the joined tracks)" : " (click at a crossing of two straights)");
(slip.type == "Rkp" ? " (click at a crossing of two straights, on the side of the joined tracks, or drag it out on a straight)" : " (click at a crossing of two straights, or drag it out on a straight)");
result.push_back(slip);
}
struct crossing
@@ -3060,7 +3194,7 @@ std::vector<editor_track::switch_template> editor_track::standard_switch_templat
plain.a = entry.half;
plain.ratio = ratio_of(entry.name);
plain.rail = std::string{entry.name}.substr(0, std::string{entry.name}.find('-'));
plain.label = std::string{"Skrz "} + entry.name + format(" a %.3f", entry.half) + " crossing (click at a crossing of two straights)";
plain.label = std::string{"Skrz "} + entry.name + format(" a %.3f", entry.half) + " crossing (click at a crossing of two straights, or drag it out on a straight)";
result.push_back(plain);
}
return result;