17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-20 13:54:53 +02:00

feat(editor): a join of ends side by side shifts the track with reverse curves over the whole gap

The reverse curves of a join start and end at the joined ends, with the
largest radius that fits, so a shift of the track spacing (4 -> 4.75 m
over 400 m) is one S instead of two curves with straights around them.
A straight or a single arc with a kink at its ends, though still under
the joints check, comes after the smooth curves among the variants; a
practically exact one stays first.

An element under 1 mm, like the straight left between the reverse
curves by the radius fit, no longer drops out of the pieces and leaves
a gap: the neighbouring piece takes it over.
This commit is contained in:
maj00r
2026-10-19 18:40:43 +02:00
parent ffdccd90ed
commit 438cb4ea3a
2 changed files with 35 additions and 4 deletions

View File

@@ -969,10 +969,12 @@ std::optional<join_result> straight_join(join_frame const &Frame)
{
auto const &a{Frame.request.start};
auto const &b{Frame.request.end};
if (std::abs(geometry::signed_angle(Frame.start_direction, Frame.chord)) > Frame.tolerance || std::abs(geometry::signed_angle(Frame.chord, Frame.end_direction)) > Frame.tolerance)
auto const kink{std::max(std::abs(geometry::signed_angle(Frame.start_direction, Frame.chord)), std::abs(geometry::signed_angle(Frame.chord, Frame.end_direction)))};
if (kink > Frame.tolerance)
return std::nullopt;
join_result straight;
straight.kind = join_kind::straight;
straight.kink = kink;
segment_data path;
path.points[segment_data::point::start] = a.position;
path.points[segment_data::point::end] = b.position;
@@ -992,10 +994,12 @@ std::optional<join_result> arc_join(join_frame const &Frame)
auto const angle{2.0 * std::atan2(std::abs(lateral), glm::dot(Frame.start_direction, Frame.chord))};
int const side{lateral > 0.0 ? 1 : -1};
auto const radius{Frame.distance * Frame.distance / (2.0 * std::abs(lateral))};
if (angle >= kPi - 1e-3 || std::abs(geometry::signed_angle(turned(Frame.start_direction, side * angle), Frame.end_direction)) > Frame.tolerance)
auto const kink{std::abs(geometry::signed_angle(turned(Frame.start_direction, side * angle), Frame.end_direction))};
if (angle >= kPi - 1e-3 || kink > Frame.tolerance)
return std::nullopt;
join_result arc;
arc.kind = join_kind::arc;
arc.kink = kink;
arc.pieces = geometry::arc_pieces(a.position, Frame.start_direction, Frame.rise / (radius * angle), radius, angle, side);
arc.pieces.back().points[segment_data::point::end] = b.position;
spread_rolls(arc.pieces, a.roll, b.roll);
@@ -1024,7 +1028,7 @@ design join_design(join_frame const &Frame, join_kind const Kind)
shape.vertices = {corner};
else
{
corner.offset = Frame.distance / 3.0;
corner.offset = Frame.distance / 4.0;
shape.vertices = {corner, corner};
}
return shape;
@@ -1103,9 +1107,11 @@ join_result join(join_request const &Request)
auto const enddirection{glm::normalize(b.direction)};
join_frame const frame{Request, startdirection, enddirection, chord, distance, glm::radians(Request.kink), std::abs(geometry::signed_angle(startdirection, enddirection)), b.position.y - a.position.y};
std::vector<join_result> candidates;
std::vector<join_result> kinked;
for (auto const &candidate : {straight_join(frame), arc_join(frame), curve_join(frame)})
if (candidate.has_value())
candidates.push_back(*candidate);
(candidate->kink > 0.1 * frame.tolerance ? kinked : candidates).push_back(*candidate);
candidates.insert(candidates.end(), kinked.begin(), kinked.end());
if (candidates.empty())
candidates.push_back(bezier_join(frame));
auto const count{static_cast<int>(candidates.size())};
@@ -1253,12 +1259,36 @@ std::vector<segment_data> pieces(result const &Result, design const &Design, std
auto const chainage_from = [&](span const &Span) { return Span.generic ? Span.start : Result.elements[Span.element].chainage + Span.from; };
auto const chainage_to = [&](span const &Span) { return Span.generic ? Span.end : Result.elements[Span.element].chainage + Span.to; };
std::vector<span> spans;
std::optional<double> lead;
for (std::size_t i = 0; i < Result.elements.size(); ++i)
{
auto const &e{Result.elements[i]};
auto const count{element_pieces(e, Design.shape, Design.transition_pieces, Design.arc_piece_angle)};
if (count == 0)
{
if (e.length <= 0.0)
continue;
if (spans.empty())
{
lead = lead.value_or(e.chainage);
continue;
}
auto &previous{spans.back()};
previous.start = chainage_from(previous);
previous.end = e.chainage + e.length;
previous.generic = true;
continue;
}
for (std::size_t j = 0; j < count; ++j)
spans.push_back({i, e.length * j / count, e.length * (j + 1) / count});
if (lead.has_value())
{
auto &first{spans[spans.size() - count]};
first.end = chainage_to(first);
first.start = *lead;
first.generic = true;
lead.reset();
}
}
double const shortest{1.0};
for (std::size_t i = 0; i < spans.size() && spans.size() > 1;)

View File

@@ -232,6 +232,7 @@ struct join_result
double cant{0.0};
double transition{0.0};
double speed{0.0};
double kink{0.0};
int variants{0};
std::vector<std::string> warnings;
std::vector<std::string> radius_warnings;