17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 02:49:19 +02:00

feat(editor): track editor driven by what the cursor is over: the outline and a label show what a click or a drag does, snapping to free ends on the screen, handles and ends dragged directly, no rotation gizmo; laying from a free end the same in every mode, straight with Shift, the curve of a switch going on past its diverging end; height 0 where there's no terrain; placed switch leaves the switch mode, double slips listed apart, a drive of the switch placed with it by the //$e switch description of its template; compound curves of any number of radii; dragged end of a straight fits the curve beside it; name of a path, followed by the memory cells and the includes taking it; turntable placed from a template described as one, tracks led out of it and the tracks around fitted to it, its positions written to the include; infrastructure bound to the path whose events read its memory cell; terrain made where the route has none and shaped to the formation under the ballast; models along the track over the selected path and on along the joined ones while dragged, rarely used settings folded; vehicles and trainsets placed on any path, switches included, facing the way the arrow shows; joined paths spread into the selection; New scenery in the File menu starts the editor again with the wizard; ortho zoom towards the cursor; scripted self tests; Polish translation

This commit is contained in:
maj00r
2026-10-06 22:19:54 +02:00
parent 0a3cd55e59
commit 24923dd472
26 changed files with 9991 additions and 468 deletions

View File

@@ -97,18 +97,34 @@ struct compound_curve
double shortened{1.0};
};
compound_curve build_compound(double const Deflection, double const Radius1, double const Radius2, double const Lengthin, double const Lengthmiddle, double const Lengthout, double const Split)
compound_curve build_compound(double const Deflection, std::vector<compound_arc> const &Arcs, double const Lengthin, double const Lengthout)
{
compound_curve result;
auto const k1{1.0 / Radius1};
auto const k2{1.0 / Radius2};
auto const transitions{(k1 * Lengthin + (k1 + k2) * Lengthmiddle + k2 * Lengthout) * 0.5};
if (Arcs.empty())
return result;
std::vector<double> curvatures;
for (auto const &arc : Arcs)
curvatures.push_back(1.0 / std::max(1.0, arc.radius));
auto transitions{curvatures.front() * Lengthin + curvatures.back() * Lengthout};
for (std::size_t i = 1; i < Arcs.size(); ++i)
transitions += (curvatures[i - 1] + curvatures[i]) * std::max(0.0, Arcs[i].transition);
transitions *= 0.5;
if (transitions > Deflection)
result.shortened = Deflection / transitions * (1.0 - 1e-9);
auto const f{result.shortened};
auto const arcs{std::max(0.0, Deflection - transitions * f)};
auto const split{std::clamp(Split, 0.0, 1.0)};
std::array<double, 3> const segments[] = {{0.0, k1, Lengthin * f}, {k1, k1, split * arcs / k1}, {k1, k2, Lengthmiddle * f}, {k2, k2, (1.0 - split) * arcs / k2}, {k2, 0.0, Lengthout * f}};
double total{0.0};
for (auto const &arc : Arcs)
total += std::max(0.0, arc.share);
std::vector<std::array<double, 3>> segments{{0.0, curvatures.front(), Lengthin * f}};
for (std::size_t i = 0; i < Arcs.size(); ++i)
{
if (i > 0)
segments.push_back({curvatures[i - 1], curvatures[i], std::max(0.0, Arcs[i].transition) * f});
auto const share{total > 0.0 ? std::max(0.0, Arcs[i].share) / total : 1.0 / static_cast<double>(Arcs.size())};
segments.push_back({curvatures[i], curvatures[i], share * arcs / curvatures[i]});
}
segments.push_back({curvatures.back(), 0.0, Lengthout * f});
double x{0.0}, y{0.0}, heading{0.0};
for (auto const &segment : segments)
{
@@ -209,8 +225,11 @@ std::size_t element_pieces(element const &Element, transition_shape const Shape,
case element_kind::spiral:
{
auto const turn{(Element.curvature_start + Element.curvature_end) * 0.5 * Element.length};
auto const count{static_cast<std::size_t>(std::max({1.0, std::ceil(turn / (Arcangle * kPi / 180.0) - 1e-9), std::ceil(Element.length / 20.0 - 1e-9)}))};
return Element.curvature_start != Element.curvature_end ? std::max<std::size_t>(count, std::max(1, Transitions)) : count;
auto const arcs{std::max(1.0, std::ceil(turn / (Arcangle * kPi / 180.0) - 1e-9))};
if (Element.curvature_start == Element.curvature_end)
return static_cast<std::size_t>(arcs);
auto const count{static_cast<std::size_t>(std::max(arcs, std::ceil(Element.length / 20.0 - 1e-9)))};
return std::max<std::size_t>(count, std::max(1, Transitions));
}
default:
return 1;
@@ -360,10 +379,17 @@ result fit_between(design const &Design)
double tangent_out{0.0};
double deflection{0.0};
bool compound{false};
double radius2{0.0};
double transition_middle{0.0};
std::vector<compound_arc> arcs;
std::function<void(double, double &, double &)> tangents;
};
auto const scaled = [](std::vector<compound_arc> Arcs, double const Scale) {
for (auto &arc : Arcs)
{
arc.radius *= Scale;
arc.transition *= Scale;
}
return Arcs;
};
std::vector<fitted> fits(count);
for (std::size_t k = 0; k < count; ++k)
{
@@ -386,16 +412,17 @@ result fit_between(design const &Design)
r.errors.emplace_back(format("Vertex %zu: a turn of about 180 or 360 degrees needs one more vertex", k + 1));
return r;
}
if (vertex.compound)
if (vertex.compound && false == vertex.arcs.empty())
{
fit.compound = true;
fit.radius2 = std::max(1.0, vertex.radius2);
fit.transition_middle = std::max(0.0, vertex.transition_middle);
fit.arcs = {{fit.radius, 0.0, vertex.share}};
for (auto const &arc : vertex.arcs)
fit.arcs.push_back({std::max(1.0, arc.radius), std::max(0.0, arc.transition), arc.share});
fit.deflection = deflection;
fit.tangents = [&, k, directionin, directionout, side](double const Radius, double &Tangentin, double &Tangentout) {
fit.tangents = [&, k, directionin, directionout, side, scaled](double const Radius, double &Tangentin, double &Tangentout) {
auto const &own{fits[k]};
auto const scale{Radius / own.radius};
auto const shape{build_compound(own.deflection, Radius, own.radius2 * scale, own.transition_in * scale, own.transition_middle * scale, own.transition_out * scale, Design.vertices[k].split)};
auto const shape{build_compound(own.deflection, scaled(own.arcs, scale), own.transition_in * scale, own.transition_out * scale)};
auto const normalin{perpendicular(directionin) * static_cast<double>(side)};
auto const end{directionin * shape.end.x + normalin * shape.end.y};
auto const along{cross(directionout, end) / cross(directionout, directionin)};
@@ -485,9 +512,8 @@ result fit_between(design const &Design)
else
{
fit.radius *= scales[k];
fit.radius2 *= scales[k];
fit.arcs = scaled(fit.arcs, scales[k]);
fit.transition_in *= scales[k];
fit.transition_middle *= scales[k];
fit.transition_out *= scales[k];
}
r.warnings.emplace_back(format("Vertex %zu: R reduced from %.1f to %.1f m, transitions to %.1f / %.1f m, to fit between the ends", k + 1, Design.vertices[k].radius, fit.radius, fit.transition_in, fit.transition_out));
@@ -525,7 +551,7 @@ result fit_between(design const &Design)
}
if (fit.compound)
{
auto const shape{build_compound(deflection, fit.radius, fit.radius2, fit.transition_in, fit.transition_middle, fit.transition_out, vertex.split)};
auto const shape{build_compound(deflection, fit.arcs, fit.transition_in, fit.transition_out)};
if (shape.shortened < 1.0)
r.warnings.emplace_back(format("Vertex %zu: transition curves shortened to fit the deflection angle", k + 1));
auto const normalin{perpendicular(directionin) * static_cast<double>(side)};
@@ -541,7 +567,10 @@ result fit_between(design const &Design)
auto const startchainage{chainage};
glm::dvec2 origin{curvestart};
glm::dvec2 heading{directionin};
auto const peak{std::max(1.0 / fit.radius, 1.0 / fit.radius2)};
auto sharpest{fit.radius};
for (auto const &arc : fit.arcs)
sharpest = std::min(sharpest, arc.radius);
auto const peak{1.0 / sharpest};
for (auto const &segment : shape.segments)
{
element piece;
@@ -568,14 +597,14 @@ result fit_between(design const &Design)
curve_report report;
report.vertex = static_cast<int>(k);
report.radius = fit.radius;
report.radius2 = fit.radius2;
for (auto const &arc : fit.arcs)
report.radii.push_back(arc.radius);
report.transition_in = fit.transition_in * shape.shortened;
report.transition_out = fit.transition_out * shape.shortened;
report.deflection = deflection;
report.arc_length = chainage - startchainage;
report.tangent_in = glm::distance(position, curvestart);
report.tangent_out = glm::distance(position, cursor);
auto const sharpest{std::min(fit.radius, fit.radius2)};
report.unbalanced = unbalanced_acceleration(Design.speed, sharpest, std::max(0.0, vertex.cant), Design.norms);
report.recommended = recommend(Design.speed, sharpest, Design.norms);
r.curves.push_back(report);