17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-31 18:09:04 +01:00

feat(editor): a slip Rkp or Rkpd bent into a curve of a side track: dragged on a curve with a slip armed it's laid out on the tangent to the curve where it was pressed and bent onto the curve as a switch laid into one is, editor_mode::frame_path laying a path of a template into the curve for both, its crossing track coming along with its ends free, ahead to the side of the drag, the slip curve of a single slip on that side, the straights of both tracks out to the joints of its template. A crossing goes only where two straights cross, a curve as sharp as the slip curves is refused. As it's dragged the view tells the radii of its tracks and slip curves, its cant and what switch_curve::check_slip says, the 11.4 pkt 4b of ST-T1-A6 among it; laid, the status the same. A slip bent into a curve isn't replaced by one laid out of straights. Selftests switchcurve, bladetips: a Rkpd 49E1-190-1:9 in R 800 m with its slip curves of R 154 and 249 m and the warnings of 11.4 pkt 4b and table 9.2, one of R 300 m in R 800 m with no warning of table 9.2, a Rkp in R 4000 m, the blades from 1.248 m on all of them; refused in R 150 m, a Skrz refused

This commit is contained in:
maj00r
2026-10-27 19:39:38 +01:00
parent 7f6a8736ca
commit 7b11db566e
6 changed files with 538 additions and 179 deletions

View File

@@ -1598,6 +1598,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri
switch_curve::switch_report curved_switch_report(std::vector<segment_data> const &Paths, double const Station, int const Direction) const;
static double nearest_station(std::vector<curve_sample> const &Frame, glm::dvec3 const &Point);
static std::vector<segment_data> curved_switch_paths(editor_track::switch_template const &Shape, std::vector<curve_sample> const &Frame, double const Station, int const Direction, int const Side);
// the path of a switch template laid into the curve: x of the template across the main track to the Side, z along it from the
// Station on, going Direction along the curve. The heights and the cant are those of the line where the point is along it
static segment_data frame_path(segment_data const &Local, std::vector<curve_sample> const &Frame, double const Station, int const Direction, int const Side);
void insert_curved_switch(int const Direction, int const Side);
// a crossover between two tracks running side by side along a curve (editormodeconnectioncurve.cpp)
struct curve_mapping;
@@ -1681,7 +1684,41 @@ class editor_mode : public application_mode, private editor_track::observer, pri
glm::dvec3 cursor_on_straight(editor_track::straight const &Line) const;
bool insert_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape);
bool replace_double_slip(TTrack &Part, editor_track::switch_template const &Shape);
std::vector<TTrack *> build_double_slip(glm::dvec3 const &PointA, glm::dvec3 const &PointB, glm::dvec3 const &PointC, glm::dvec3 const &PointD, glm::dvec2 const &Crossing, double const Height, glm::dvec2 const &First, glm::dvec2 const &Second, double const Angle, editor_track::slip_curve const &Curve, TTrack &Style, std::string const &Type = "Rkpd");
// Bend: what a path of the slip laid out as on straights turns into where it's bent into a curve
std::vector<TTrack *> build_double_slip(glm::dvec3 const &PointA, glm::dvec3 const &PointB, glm::dvec3 const &PointC, glm::dvec3 const &PointD, glm::dvec2 const &Crossing, double const Height, glm::dvec2 const &First, glm::dvec2 const &Second, double const Angle, editor_track::slip_curve const &Curve, TTrack &Style, std::string const &Type = "Rkpd",
std::function<segment_data(segment_data const &)> const &Bend = {});
// a slip switch bent into a curve of a side track (ST-T1-A6 11.4 pkt 4b): laid out on the tangent to the curve at its crossing, where
// it was pressed, and bent onto the curve as a switch laid into it is, its crossing track coming along with its ends free. Direction
// and Side as of a switch dragged into a curve: the crossing track goes ahead to that side, through the quarter of the cursor, and the
// slip curve of a single slip lies on that side
struct curved_slip
{
std::string problem;
std::string snapped; // what the ends of the slip were drawn to
double middle{0.0}; // the station of the crossing along the curve
double half{0.0}; // from the crossing to the ends of the slip
double reach{0.0}; // from the crossing to the joints of the template, the straights of both tracks laid out that far
int direction{1};
double angle{0.0};
editor_track::slip_curve curve;
// as the slip is laid out on the tangent, before it's bent: its crossing, its tracks going Direction along the curve, its ends
glm::dvec2 crossing{0.0};
glm::dvec2 first{0.0, 1.0};
glm::dvec2 second{0.0, 1.0};
std::array<glm::dvec3, 4> points{};
};
bool plan_curved_slip(editor_track::switch_template const &Shape, int const Direction, int const Side, curved_slip &Plan) const;
// the path of the slip laid out on the tangent, bent onto the curve
segment_data bent_slip_path(curved_slip const &Plan, segment_data const &Path) const;
// the two tracks through the slip and its slip curves, each from end to end, bent: the first track, the slip curve leaving it at its
// start, the second track, the other slip curve of a double slip
std::vector<segment_data> curved_slip_lines(curved_slip const &Plan, std::string const &Type) const;
// the straights of both tracks from the ends of the slip out to the joints of the template, bent
std::vector<segment_data> curved_slip_reaches(curved_slip const &Plan) const;
switch_curve::slip_report curved_slip_report(curved_slip const &Plan, std::string const &Type) const;
// the radii of the slip and its cant, as the view and the status tell them
static std::vector<std::string> slip_report_lines(switch_curve::slip_report const &Report);
void insert_curved_slip(int const Direction, int const Side);
bool start_extend(editor_track::snap_target const &End);
void finish_extend();
std::vector<segment_data> extend_pieces() const;

View File

@@ -68,6 +68,20 @@ editor_mode::selftest_result editor_mode::selftest_switch(std::string const &com
text += format(", drive %.3f m from the joint", geometry::plan_distance(found->standing.location, start));
else
text += ", no drive";
// how far apart the rail of the blade and the one it lies at are along the path, where the blades of a slip are shaped
if (track->DoubleSlip())
for (int path = 0; path < 2; ++path)
{
auto const &shape{track->blade(path)};
auto const gap = [&](double const At) {
auto const &gauge{shape.gauge};
auto const next{std::find_if(gauge.begin(), gauge.end(), [&](glm::dvec2 const &Sample) { return Sample.x >= At; })};
return next == gauge.end() ? (gauge.empty() ? -1.0 : gauge.back().y) : next->y;
};
text += format(", path %d %s side %d gaps", path, shape.valid ? "shaped" : "not shaped", track->SwitchExtension->RightSwitch == (path == 0) ? 1 : -1);
for (auto const at : {0.354, 1.0, 1.248, 2.0, 4.0, 6.0, 8.0, 9.4})
text += format(" %.1f:%.4f", at, gap(at));
}
WriteLog(text);
}
return selftest_result::handled;
@@ -575,7 +589,29 @@ editor_mode::selftest_result editor_mode::selftest_switch(std::string const &com
tool.frame_tracks = chain.tracks;
tool.curved = true;
tool.along = nearest_station(tool.frame, point);
insert_curved_switch(direction, side);
tool.pressed = tool.along;
// a slip bent into the curve with its crossing track, its paths and its record logged
if (tool.templates[tool.armed].double_slip)
{
insert_curved_slip(direction, side);
if (auto const *part{selected_track()}; part != nullptr && part->DoubleSlip())
if (auto const known{switch_store::named(*part)})
{
WriteLog(format("SELFTEST switchcurve slip %s: %s, %zu parts", known->name.c_str(), switch_store::label(*known).c_str(), known->parts.size()));
for (auto const &name : known->parts)
if (auto const *track{simulation::Paths.find(name)})
for (std::size_t i = 0; i < track->m_paths.size(); ++i)
{
auto const &path{track->m_paths[i]};
auto const &a{path.points[segment_data::point::start]};
auto const &b{path.points[segment_data::point::end]};
WriteLog(format("SELFTEST switchcurve slip path %s/%zu: %.3f %.3f %.3f -> %.3f %.3f %.3f, R %.1f, %zu pieces, rolls %.2f %.2f", name.c_str(), i, a.x, a.y, a.z, b.x, b.y, b.z, static_cast<double>(path.radius), path.pieces.size(),
static_cast<double>(path.rolls[0]), static_cast<double>(path.rolls[1])));
}
}
}
else
insert_curved_switch(direction, side);
}
else
{

View File

@@ -59,6 +59,16 @@ std::vector<segment_data> editor_mode::switch_preview() const
int forward;
int side;
curved_switch_pose(forward, side);
if (tool.templates[tool.armed].double_slip)
{
curved_slip plan;
if (false == plan_curved_slip(tool.templates[tool.armed], forward, side, plan))
return {};
auto lines{curved_slip_lines(plan, tool.templates[tool.armed].type)};
auto const reaches{curved_slip_reaches(plan)};
lines.insert(lines.end(), reaches.begin(), reaches.end());
return lines;
}
return curved_switch_paths(tool.templates[tool.armed], tool.frame, curved_switch_start(forward), forward, side);
}
if (tool.templates[tool.armed].double_slip)
@@ -104,9 +114,10 @@ bool editor_mode::start_switch_placement()
if (current_straight().tracks.empty())
{
auto *track{selected};
if (tool.templates[tool.armed].double_slip)
// a slip is bent into the curve, in a side track only, a crossing goes only where two straights cross
if (tool.templates[tool.armed].double_slip && tool.templates[tool.armed].type == "Skrz")
{
tool.status = refused(STR_C("A double slip goes only into a straight crossing another one"));
tool.status = refused(STR_C("A crossing goes only where two straights cross"));
return false;
}
editor_track::curve curve;
@@ -216,6 +227,11 @@ void editor_mode::finish_switch_placement()
int forward;
int side;
curved_switch_pose(forward, side);
if (tool.templates[tool.armed].double_slip)
{
insert_curved_slip(forward, side);
return;
}
tool.along = curved_switch_start(forward);
insert_curved_switch(forward, side);
return;
@@ -832,83 +848,97 @@ std::vector<TTrack *> editor_mode::link_tracks(editor_track::snap_target const &
return pieces;
}
std::vector<TTrack *> editor_mode::build_double_slip(glm::dvec3 const &PointA, glm::dvec3 const &PointB, glm::dvec3 const &PointC, glm::dvec3 const &PointD, glm::dvec2 const &Crossing, double const Height, glm::dvec2 const &First, glm::dvec2 const &Second, double const Angle, editor_track::slip_curve const &Curve, TTrack &Style, std::string const &Type)
namespace
{
auto const at = [](glm::dvec2 const &Planar, double const Y) { return glm::dvec3{Planar.x, Y, Planar.y}; };
auto const straight = [](glm::dvec3 const &Start, glm::dvec3 const &End) {
segment_data straight_path(glm::dvec3 const &Start, glm::dvec3 const &End)
{
segment_data path;
path.points[segment_data::point::start] = Start;
path.points[segment_data::point::end] = End;
return path;
}
// the part of the slip curve from Start along Heading, turning by Turn to Finish, between the distances From and To along it; where it
// changes the radius made of pieces, with one curve from end to end for the versions which know no pieces
segment_data slip_arc(editor_track::slip_curve const &Curve, glm::dvec3 const &Start, glm::dvec2 const &Heading, double const Turn, glm::dvec3 const &Finish, double const From, double const To)
{
auto const whole{Curve.length()};
auto const place = [&](double const Along) {
auto const local{Curve.point(Along)};
auto const planar{plan_of(Start) + Heading * local.x + glm::dvec2{-Heading.y, Heading.x} * (Turn * local.y)};
return glm::dvec3{planar.x, Start.y + (Finish.y - Start.y) * Along / whole, planar.y};
};
auto const bend = [&](double const Begin, double const End, double const Radius, double const Handle0, double const Handle1) {
auto const d0{turned(Heading, Turn * Curve.heading(Begin))};
auto const d3{turned(Heading, Turn * Curve.heading(End))};
segment_data path;
path.points[segment_data::point::start] = Start;
path.points[segment_data::point::end] = End;
path.points[segment_data::point::start] = place(Begin);
path.points[segment_data::point::end] = place(End);
if (Radius > 0.0)
{
path.points[segment_data::point::control1] = {d0.x * Handle0, 0.0, d0.y * Handle0};
path.points[segment_data::point::control2] = {-d3.x * Handle1, 0.0, -d3.y * Handle1};
}
path.radius = static_cast<float>(Radius);
return path;
};
std::vector<std::pair<double, double>> zones;
for (auto const &zone : {std::pair{Curve.lead, 0.0}, {Curve.lead + Curve.ends, Curve.radius}, {Curve.lead + Curve.ends + Curve.between, Curve.middle}, {whole - Curve.lead, Curve.radius}, {whole, 0.0}})
{
if (zone.first <= (zones.empty() ? 0.0 : zones.back().first) + 1e-9)
continue;
if (false == zones.empty() && zone.second == zones.back().second)
zones.back().first = zone.first;
else
zones.push_back(zone);
}
// broken at the tips of the blades of the tables, so the model of the switch finds them there
if (Curve.tip > 0.0)
for (auto const cut : {Curve.tip, whole - Curve.tip})
for (std::size_t i = 0; i < zones.size(); ++i)
if (cut > (i > 0 ? zones[i - 1].first : 0.0) + 0.05 && cut < zones[i].first - 0.05)
{
zones.insert(zones.begin() + i, {cut, zones[i].second});
break;
}
std::vector<segment_data> pieces;
auto from{From};
for (auto const &[to, radius] : zones)
{
auto const upto{std::min(To, to)};
if (upto <= from + 1e-6)
continue;
auto const handle{radius > 0.0 ? 4.0 / 3.0 * std::tan((upto - from) / radius / 4.0) * radius : 0.0};
pieces.push_back(bend(from, upto, radius, handle, handle));
from = upto;
}
if (pieces.size() == 1)
return pieces.front();
// the handles of the curve from end to end reach 2/3 of the way to the crossing of the tangents
auto const chord{plan_of(place(To) - place(From))};
auto const d0{turned(Heading, Turn * Curve.heading(From))};
auto const d3{turned(Heading, Turn * Curve.heading(To))};
auto const sine{cross(d0, d3)};
auto path{bend(From, To, Curve.radius, 2.0 / 3.0 * cross(chord, d3) / sine, 2.0 / 3.0 * cross(d0, chord) / sine)};
path.pieces = std::move(pieces);
return path;
}
} // namespace
std::vector<TTrack *> editor_mode::build_double_slip(glm::dvec3 const &PointA, glm::dvec3 const &PointB, glm::dvec3 const &PointC, glm::dvec3 const &PointD, glm::dvec2 const &Crossing, double const Height, glm::dvec2 const &First, glm::dvec2 const &Second, double const Angle, editor_track::slip_curve const &Curve, TTrack &Style, std::string const &Type, std::function<segment_data(segment_data const &)> const &Bend)
{
auto const at = [](glm::dvec2 const &Planar, double const Y) { return glm::dvec3{Planar.x, Y, Planar.y}; };
auto const straight = [](glm::dvec3 const &Start, glm::dvec3 const &End) { return straight_path(Start, End); };
// the ends of the two diagonals at the crossing are kept apart further than the simulation joins the ends, 2 cm
auto const halfsine{std::sqrt(std::max(1e-6, (1.0 - glm::dot(First, Second)) * 0.5))};
auto const gap{std::max(0.25, 0.06 / halfsine)};
auto const side{cross(First, Second) > 0.0 ? 1.0 : -1.0};
auto const whole{Curve.length()};
// the part of the slip curve from Start along Heading, turning by Turn to Finish, between the distances From and To along it;
// where it changes the radius made of pieces, with one curve from end to end for the versions which know no pieces
auto const arc = [&](glm::dvec3 const &Start, glm::dvec2 const &Heading, double const Turn, glm::dvec3 const &Finish, double const From, double const To) {
auto const place = [&](double const Along) {
auto const local{Curve.point(Along)};
auto const planar{plan_of(Start) + Heading * local.x + glm::dvec2{-Heading.y, Heading.x} * (Turn * local.y)};
return glm::dvec3{planar.x, Start.y + (Finish.y - Start.y) * Along / whole, planar.y};
};
auto const bend = [&](double const Begin, double const End, double const Radius, double const Handle0, double const Handle1) {
auto const d0{turned(Heading, Turn * Curve.heading(Begin))};
auto const d3{turned(Heading, Turn * Curve.heading(End))};
segment_data path;
path.points[segment_data::point::start] = place(Begin);
path.points[segment_data::point::end] = place(End);
if (Radius > 0.0)
{
path.points[segment_data::point::control1] = {d0.x * Handle0, 0.0, d0.y * Handle0};
path.points[segment_data::point::control2] = {-d3.x * Handle1, 0.0, -d3.y * Handle1};
}
path.radius = static_cast<float>(Radius);
return path;
};
std::vector<std::pair<double, double>> zones;
for (auto const &zone : {std::pair{Curve.lead, 0.0}, {Curve.lead + Curve.ends, Curve.radius}, {Curve.lead + Curve.ends + Curve.between, Curve.middle}, {whole - Curve.lead, Curve.radius}, {whole, 0.0}})
{
if (zone.first <= (zones.empty() ? 0.0 : zones.back().first) + 1e-9)
continue;
if (false == zones.empty() && zone.second == zones.back().second)
zones.back().first = zone.first;
else
zones.push_back(zone);
}
// broken at the tips of the blades of the tables, so the model of the switch finds them there
if (Curve.tip > 0.0)
for (auto const cut : {Curve.tip, whole - Curve.tip})
for (std::size_t i = 0; i < zones.size(); ++i)
if (cut > (i > 0 ? zones[i - 1].first : 0.0) + 0.05 && cut < zones[i].first - 0.05)
{
zones.insert(zones.begin() + i, {cut, zones[i].second});
break;
}
std::vector<segment_data> pieces;
auto from{From};
for (auto const &[to, radius] : zones)
{
auto const upto{std::min(To, to)};
if (upto <= from + 1e-6)
continue;
auto const handle{radius > 0.0 ? 4.0 / 3.0 * std::tan((upto - from) / radius / 4.0) * radius : 0.0};
pieces.push_back(bend(from, upto, radius, handle, handle));
from = upto;
}
if (pieces.size() == 1)
return pieces.front();
// the handles of the curve from end to end reach 2/3 of the way to the crossing of the tangents
auto const chord{plan_of(place(To) - place(From))};
auto const d0{turned(Heading, Turn * Curve.heading(From))};
auto const d3{turned(Heading, Turn * Curve.heading(To))};
auto const sine{cross(d0, d3)};
auto path{bend(From, To, Curve.radius, 2.0 / 3.0 * cross(chord, d3) / sine, 2.0 / 3.0 * cross(d0, chord) / sine)};
path.pieces = std::move(pieces);
return path;
};
auto const arc = [&](glm::dvec3 const &Start, glm::dvec2 const &Heading, double const Turn, glm::dvec3 const &Finish, double const From, double const To) { return slip_arc(Curve, Start, Heading, Turn, Finish, From, To); };
// laid out on a straight, bent where it goes into a curve
auto const laid = [&](segment_data const &Path) { return Bend ? Bend(Path) : Path; };
auto const back = [](segment_data const &Path) {
auto result{reversed(Path)};
std::reverse(result.pieces.begin(), result.pieces.end());
@@ -935,8 +965,8 @@ std::vector<TTrack *> editor_mode::build_double_slip(glm::dvec3 const &PointA, g
if (Type == "Skrz")
{
// the two tracks just cross, their ends far from the crossing
parts.push_back(editor_track::create_path(Style, straight(PointA, PointB)));
parts.push_back(editor_track::create_path(Style, straight(PointC, PointD)));
parts.push_back(editor_track::create_path(Style, laid(straight(PointA, PointB))));
parts.push_back(editor_track::create_path(Style, laid(straight(PointC, PointD))));
parts.erase(std::remove(parts.begin(), parts.end(), nullptr), parts.end());
record_slip(parts, "crossing", Type, 0.0, Angle, base);
return parts;
@@ -944,21 +974,21 @@ std::vector<TTrack *> editor_mode::build_double_slip(glm::dvec3 const &PointA, g
editor_track::switch_template shape;
shape.length = glm::distance(plan_of(PointA), Crossing);
bool const single{Type == "Rkp"};
parts.push_back(editor_track::create_switch(shape, {straight(PointA, centreA), arc(PointA, First, side, PointD, 0.0, middle - split)}, Style, base + "_a"));
parts.push_back(editor_track::create_switch(shape, {laid(straight(PointA, centreA)), laid(arc(PointA, First, side, PointD, 0.0, middle - split))}, Style, base + "_a"));
if (single)
parts.push_back(editor_track::create_path(Style, straight(PointB, centreB)));
parts.push_back(editor_track::create_path(Style, laid(straight(PointB, centreB))));
else
parts.push_back(editor_track::create_switch(shape, {straight(PointB, centreB), back(arc(PointC, Second, -side, PointB, middle + split, whole))}, Style, base + "_b"));
parts.push_back(editor_track::create_switch(shape, {laid(straight(PointB, centreB)), laid(back(arc(PointC, Second, -side, PointB, middle + split, whole)))}, Style, base + "_b"));
if (single)
parts.push_back(editor_track::create_path(Style, straight(PointC, centreC)));
parts.push_back(editor_track::create_path(Style, laid(straight(PointC, centreC))));
else
parts.push_back(editor_track::create_switch(shape, {straight(PointC, centreC), arc(PointC, Second, -side, PointB, 0.0, middle - split)}, Style, base + "_c"));
parts.push_back(editor_track::create_switch(shape, {straight(PointD, centreD), back(arc(PointA, First, side, PointD, middle + split, whole))}, Style, base + "_d"));
parts.push_back(editor_track::create_path(Style, straight(centreA, centreB)));
parts.push_back(editor_track::create_path(Style, straight(centreC, centreD)));
parts.push_back(editor_track::create_path(Style, arc(PointA, First, side, PointD, middle - split, middle + split)));
parts.push_back(editor_track::create_switch(shape, {laid(straight(PointC, centreC)), laid(arc(PointC, Second, -side, PointB, 0.0, middle - split))}, Style, base + "_c"));
parts.push_back(editor_track::create_switch(shape, {laid(straight(PointD, centreD)), laid(back(arc(PointA, First, side, PointD, middle + split, whole)))}, Style, base + "_d"));
parts.push_back(editor_track::create_path(Style, laid(straight(centreA, centreB))));
parts.push_back(editor_track::create_path(Style, laid(straight(centreC, centreD))));
parts.push_back(editor_track::create_path(Style, laid(arc(PointA, First, side, PointD, middle - split, middle + split))));
if (false == single)
parts.push_back(editor_track::create_path(Style, arc(PointC, Second, -side, PointB, middle - split, middle + split)));
parts.push_back(editor_track::create_path(Style, laid(arc(PointC, Second, -side, PointB, middle - split, middle + split))));
parts.erase(std::remove(parts.begin(), parts.end(), nullptr), parts.end());
record_slip(parts, "slip", Type, Curve.radius, Angle, base, Curve.middle);
return parts;
@@ -1017,15 +1047,20 @@ bool editor_mode::plan_double_slip(editor_track::straight const &Line, glm::dvec
}
}
}
// a double slip nearer the cursor than any crossing is replaced by the one chosen
// a double slip nearer the cursor than any crossing is replaced by the one chosen, one bent into a curve isn't: it's laid out of
// straights
if (Shape.type == "Rkpd")
{
TTrack *slip{nullptr};
double closest{25.0};
auto const bent = [](TTrack const &Track) {
auto const known{switch_store::named(Track)};
return known.has_value() && known->main_radius != 0.0;
};
for (auto *section : simulation::Region->sections(Point, 40.f))
for (auto const &cell : section->m_cells)
for (auto *track : cell.m_paths)
if (track->eType == tt_Switch && false == track->m_editorremoved && track->DoubleSlip() && glm::distance(plan_of(track->location()), plan_of(Point)) < closest)
if (track->eType == tt_Switch && false == track->m_editorremoved && track->DoubleSlip() && glm::distance(plan_of(track->location()), plan_of(Point)) < closest && false == bent(*track))
{
closest = glm::distance(plan_of(track->location()), plan_of(Point));
slip = track;
@@ -1486,53 +1521,60 @@ double editor_mode::curved_switch_start(int const Direction, std::string *What)
return start;
}
namespace
{
// a path from its start, in the plan, a point every quarter of a metre
std::vector<switch_curve::track_point> plan_points(segment_data const &Path)
{
std::vector<switch_curve::track_point> points;
auto const add = [&](segment_data const &Piece) {
bezier const curve{Piece};
auto const steps{std::max(4, static_cast<int>(std::ceil(curve.plan_length() / 0.25)))};
for (int k = points.empty() ? 0 : 1; k <= steps; ++k)
{
auto const t{static_cast<double>(k) / steps};
auto const position{plan_of(curve.point(t))};
auto const tangent{plan_of(curve.first(t))};
switch_curve::track_point point;
point.heading = std::atan2(tangent.y, tangent.x);
if (false == points.empty())
{
point.station = points.back().station + glm::distance(position, plan_of(curve.point(static_cast<double>(k - 1) / steps)));
point.heading = points.back().heading + std::remainder(point.heading - points.back().heading, 2.0 * glm::pi<double>());
}
points.push_back(point);
}
};
if (Path.pieces.empty())
add(Path);
for (auto const &piece : Path.pieces)
add(piece);
return points;
}
} // namespace
switch_curve::switch_report editor_mode::curved_switch_report(std::vector<segment_data> const &Paths, double const Station, int const Direction) const
{
auto const &tool{m_switch};
if (Paths.size() < 2 || tool.armed < 0 || tool.armed >= static_cast<int>(tool.templates.size()))
return {};
// a path of the switch from its start, in the plan
auto const along = [](segment_data const &Path) {
std::vector<switch_curve::track_point> points;
auto const add = [&](segment_data const &Piece) {
bezier const curve{Piece};
auto const steps{std::max(4, static_cast<int>(std::ceil(curve.plan_length() / 0.25)))};
for (int k = points.empty() ? 0 : 1; k <= steps; ++k)
{
auto const t{static_cast<double>(k) / steps};
auto const position{plan_of(curve.point(t))};
auto const tangent{plan_of(curve.first(t))};
switch_curve::track_point point;
point.heading = std::atan2(tangent.y, tangent.x);
if (false == points.empty())
{
point.station = points.back().station + glm::distance(position, plan_of(curve.point(static_cast<double>(k - 1) / steps)));
point.heading = points.back().heading + std::remainder(point.heading - points.back().heading, 2.0 * glm::pi<double>());
}
points.push_back(point);
}
};
if (Path.pieces.empty())
add(Path);
for (auto const &piece : Path.pieces)
add(piece);
return points;
};
auto const &shape{tool.templates[tool.armed]};
return switch_curve::check(along(Paths[0]), along(Paths[1]), frame_points(tool.frame), Station, Station + Direction * shape.length, shape.radius, shape.ratio);
return switch_curve::check(plan_points(Paths[0]), plan_points(Paths[1]), frame_points(tool.frame), Station, Station + Direction * shape.length, shape.radius, shape.ratio);
}
std::vector<segment_data> editor_mode::curved_switch_paths(editor_track::switch_template const &Shape, std::vector<curve_sample> const &Frame, double const Station, int const Direction, int const Side)
segment_data editor_mode::frame_path(segment_data const &Local, std::vector<curve_sample> const &Frame, double const Station, int const Direction, int const Side)
{
auto const map = [&](glm::dvec3 const &Local, glm::dvec2 &Tangent, glm::dvec2 const &Localtangent) {
auto const sample{sample_at(Frame, Station + Direction * Local.z)};
auto const map = [&](glm::dvec3 const &Point, glm::dvec2 &Tangent, glm::dvec2 const &Localtangent) {
auto const sample{sample_at(Frame, Station + Direction * Point.z)};
auto const along{sample.tangent * static_cast<double>(Direction)};
glm::dvec2 const across{glm::dvec2{-along.y, along.x} * static_cast<double>(Side)};
Tangent = glm::normalize(along * Localtangent.y + across * Localtangent.x);
auto const planar{plan_of(sample.position) + across * Local.x};
auto const planar{plan_of(sample.position) + across * Point.x};
auto const cant{glm::radians(static_cast<double>(sample.roll) * Direction)};
auto const cone{-Local.x * Side * std::tan(cant)};
return glm::dvec3{planar.x, sample.position.y + Local.y + cone, planar.y};
auto const cone{-Point.x * Side * std::tan(cant)};
return glm::dvec3{planar.x, sample.position.y + Point.y + cone, planar.y};
};
auto const hermite = [](glm::dvec3 const &Start, glm::dvec2 const &Startdirection, glm::dvec3 const &End, glm::dvec2 const &Enddirection) {
auto const handle{plan_distance(Start, End) / 3.0};
@@ -1543,47 +1585,49 @@ std::vector<segment_data> editor_mode::curved_switch_paths(editor_track::switch_
path.points[segment_data::point::control2] = {-Enddirection.x * handle, -(End.y - Start.y) / 3.0, -Enddirection.y * handle};
return path;
};
auto const bend = [&](segment_data const &Local) {
auto const &start{Local.points[segment_data::point::start]};
auto const &end{Local.points[segment_data::point::end]};
auto const &control1{Local.points[segment_data::point::control1]};
auto const &control2{Local.points[segment_data::point::control2]};
glm::dvec2 const chord{end.x - start.x, end.z - start.z};
auto const localstart{glm::normalize(control1 != glm::dvec3{} ? plan_of(control1) : chord)};
auto const localend{glm::normalize(control2 != glm::dvec3{} ? -plan_of(control2) : chord)};
glm::dvec2 startdirection;
glm::dvec2 enddirection;
auto const p0{map(start, startdirection, localstart)};
auto const p3{map(end, enddirection, localend)};
auto path{hermite(p0, startdirection, p3, enddirection)};
{
auto const cone = [&](double const T) {
auto const point{bezier{path}.point(T)};
auto const sample{sample_at(Frame, nearest_station(Frame, point))};
auto const lateral{glm::dot(plan_of(point) - plan_of(sample.position), glm::dvec2{-sample.tangent.y, sample.tangent.x})};
return sample.position.y - lateral * std::tan(glm::radians(static_cast<double>(sample.roll)));
};
auto const h1{cone(1.0 / 3.0)};
auto const h2{cone(2.0 / 3.0)};
auto const y1{3.0 * h1 - 1.5 * h2 - 5.0 / 6.0 * p0.y + p3.y / 3.0};
auto const y2{3.0 * h2 - 1.5 * h1 + p0.y / 3.0 - 5.0 / 6.0 * p3.y};
path.points[segment_data::point::control1].y = y1 - p0.y;
path.points[segment_data::point::control2].y = y2 - p3.y;
}
auto const rollstart{sample_at(Frame, Station + Direction * start.z).roll * Direction};
auto const rollend{sample_at(Frame, Station + Direction * end.z).roll * Direction};
path.rolls = {static_cast<float>(rollstart), static_cast<float>(rollend)};
path.radius = Local.radius;
{
bezier const curve{path};
auto const begin{plan_of(curve.first(0.0))};
auto const finish{plan_of(curve.first(1.0))};
auto const turn{glm::length(begin) > 1e-9 && glm::length(finish) > 1e-9 ? geometry::signed_angle(begin, finish) : 0.0};
if (Local.radius != 0.f && std::abs(turn) > 1e-6)
path.radius = static_cast<float>(std::copysign(curve.plan_length() / std::abs(turn), static_cast<double>(Local.radius)));
}
return path;
};
auto const &start{Local.points[segment_data::point::start]};
auto const &end{Local.points[segment_data::point::end]};
auto const &control1{Local.points[segment_data::point::control1]};
auto const &control2{Local.points[segment_data::point::control2]};
glm::dvec2 const chord{end.x - start.x, end.z - start.z};
auto const localstart{glm::normalize(control1 != glm::dvec3{} ? plan_of(control1) : chord)};
auto const localend{glm::normalize(control2 != glm::dvec3{} ? -plan_of(control2) : chord)};
glm::dvec2 startdirection;
glm::dvec2 enddirection;
auto const p0{map(start, startdirection, localstart)};
auto const p3{map(end, enddirection, localend)};
auto path{hermite(p0, startdirection, p3, enddirection)};
{
auto const cone = [&](double const T) {
auto const point{bezier{path}.point(T)};
auto const sample{sample_at(Frame, nearest_station(Frame, point))};
auto const lateral{glm::dot(plan_of(point) - plan_of(sample.position), glm::dvec2{-sample.tangent.y, sample.tangent.x})};
return sample.position.y - lateral * std::tan(glm::radians(static_cast<double>(sample.roll)));
};
auto const h1{cone(1.0 / 3.0)};
auto const h2{cone(2.0 / 3.0)};
auto const y1{3.0 * h1 - 1.5 * h2 - 5.0 / 6.0 * p0.y + p3.y / 3.0};
auto const y2{3.0 * h2 - 1.5 * h1 + p0.y / 3.0 - 5.0 / 6.0 * p3.y};
path.points[segment_data::point::control1].y = y1 - p0.y;
path.points[segment_data::point::control2].y = y2 - p3.y;
}
auto const rollstart{sample_at(Frame, Station + Direction * start.z).roll * Direction};
auto const rollend{sample_at(Frame, Station + Direction * end.z).roll * Direction};
path.rolls = {static_cast<float>(rollstart), static_cast<float>(rollend)};
path.radius = Local.radius;
{
bezier const curve{path};
auto const begin{plan_of(curve.first(0.0))};
auto const finish{plan_of(curve.first(1.0))};
auto const turn{glm::length(begin) > 1e-9 && glm::length(finish) > 1e-9 ? geometry::signed_angle(begin, finish) : 0.0};
if (Local.radius != 0.f && std::abs(turn) > 1e-6)
path.radius = static_cast<float>(std::copysign(curve.plan_length() / std::abs(turn), static_cast<double>(Local.radius)));
}
return path;
}
std::vector<segment_data> editor_mode::curved_switch_paths(editor_track::switch_template const &Shape, std::vector<curve_sample> const &Frame, double const Station, int const Direction, int const Side)
{
std::vector<segment_data> locals{Shape.local.begin(), Shape.local.end()};
if (Shape.twofold())
{
@@ -1594,9 +1638,9 @@ std::vector<segment_data> editor_mode::curved_switch_paths(editor_track::switch_
for (std::size_t i = 0; i < locals.size(); ++i)
{
auto const &local{locals[i]};
auto path{bend(local)};
auto path{frame_path(local, Frame, Station, Direction, Side)};
for (auto const &piece : local.pieces)
path.pieces.push_back(bend(piece));
path.pieces.push_back(frame_path(piece, Frame, Station, Direction, Side));
// the main tracks follow the curve they are laid into
if (i % 2 == 0)
path.radius = sample_at(Frame, Station + Direction * 0.5 * (local.points[segment_data::point::start].z + local.points[segment_data::point::end].z)).radius;
@@ -1672,3 +1716,177 @@ void editor_mode::insert_curved_switch(int const Direction, int const Side)
straight_refresh();
switch_placed(placed);
}
bool editor_mode::plan_curved_slip(editor_track::switch_template const &Shape, int const Direction, int const Side, curved_slip &Plan) const
{
auto const &tool{m_switch};
Plan = curved_slip{};
// a crossing has no slip curves to bend, it goes only where two straights cross
if (Shape.type == "Skrz")
{
Plan.problem = STR_C("A crossing goes only where two straights cross");
return false;
}
if (tool.frame.size() < 2 || tool.frame_tracks.empty())
return false;
auto const *gauged{tool.frame_tracks.front()};
auto const angle{std::atan(1.0 / (Shape.ratio > 0.0 ? Shape.ratio : 9.0))};
auto const blade{switch_design::secant_blade(Shape.rail, Shape.radius, Shape.ratio)};
auto curve{editor_track::slip_of(Shape.radius, Shape.middle, angle, 0.5 * std::abs(gauged->fTrackWidth) + gauged->rail_dimensions().head + 0.002, blade.first, blade.second)};
curve.tip = switch_design::cut_tip(Shape.rail, curve.radius);
auto const half{curve.tangent()};
auto const total{tool.frame.back().station};
if (2.0 * half > total - 0.02)
{
Plan.problem = format(STR_C("The curve is %.1f m long, too short for the %.1f m switch"), total, 2.0 * half);
return false;
}
// the crossing where it was pressed, the ends of the slip drawn to the ends of the cant ramps as the ends of a switch are, the
// whole of it on the curve
auto const points{frame_points(tool.frame)};
auto const start{switch_curve::snapped_start(switch_curve::ramp_ends(points), tool.pressed - Direction * half, 2.0 * half, Direction, 3.0, &Plan.snapped)};
auto const middle{std::clamp(start + Direction * half, half, total - half)};
if (std::abs(middle - (start + Direction * half)) > 1e-6)
Plan.snapped.clear();
// bent with a curve as sharp as its slip curves, the outer one would turn the other way
double sharpest{0.0};
for (auto s{middle - half}; s <= middle + half + 1e-9; s += 0.5)
sharpest = std::max(sharpest, std::abs(switch_curve::curvature_at(points, s)));
if (sharpest * curve.radius > 1.0 - 1e-6)
{
Plan.problem = format(STR_C("The curve of R %.0f m is as sharp as the slip curves of R %.0f m or sharper, the slip isn't bent into it"), 1.0 / sharpest, curve.radius);
return false;
}
auto const sample{sample_at(tool.frame, middle)};
Plan.middle = middle;
Plan.half = half;
// the straights of both tracks out to the joints of the template, as far as the curve goes on
Plan.reach = std::max(half, std::min(Shape.a, std::min(middle, total - middle) - 0.02));
Plan.direction = Direction;
Plan.angle = angle;
Plan.curve = curve;
Plan.crossing = plan_of(sample.position);
Plan.first = sample.tangent * static_cast<double>(Direction);
glm::dvec2 const left{-Plan.first.y, Plan.first.x};
Plan.second = Plan.first * std::cos(angle) + left * (static_cast<double>(Side) * std::sin(angle));
auto const at = [](glm::dvec2 const &Planar) { return glm::dvec3{Planar.x, 0.0, Planar.y}; };
Plan.points = {at(Plan.crossing - Plan.first * half), at(Plan.crossing + Plan.first * half), at(Plan.crossing - Plan.second * half), at(Plan.crossing + Plan.second * half)};
return true;
}
segment_data editor_mode::bent_slip_path(curved_slip const &Plan, segment_data const &Path) const
{
auto const &frame{m_switch.frame};
// in the plan of a template: x across the tangent, to the left of the way the slip goes along the curve, z along it from the crossing
glm::dvec2 const left{-Plan.first.y, Plan.first.x};
auto const local = [&](segment_data const &Piece) {
auto result{Piece};
result.pieces.clear();
for (auto const index : {segment_data::point::start, segment_data::point::end})
{
auto const offset{plan_of(Piece.points[index]) - Plan.crossing};
result.points[index] = {glm::dot(offset, left), Piece.points[index].y, glm::dot(offset, Plan.first)};
}
for (auto const index : {segment_data::point::control1, segment_data::point::control2})
if (auto const &handle{Piece.points[index]}; handle != glm::dvec3{})
result.points[index] = {glm::dot(plan_of(handle), left), handle.y, glm::dot(plan_of(handle), Plan.first)};
return result;
};
auto const bent = [&](segment_data const &Piece) {
auto const flat{local(Piece)};
auto result{frame_path(flat, frame, Plan.middle, Plan.direction, 1)};
// the straights of the slip follow the curve they're bent with
if (Piece.radius == 0.f)
result.radius = sample_at(frame, Plan.middle + Plan.direction * 0.5 * (flat.points[segment_data::point::start].z + flat.points[segment_data::point::end].z)).radius;
return result;
};
auto result{bent(Path)};
for (auto const &piece : Path.pieces)
result.pieces.push_back(bent(piece));
return result;
}
std::vector<segment_data> editor_mode::curved_slip_lines(curved_slip const &Plan, std::string const &Type) const
{
auto const &[a, b, c, d]{Plan.points};
auto const side{cross(Plan.first, Plan.second) > 0.0 ? 1.0 : -1.0};
auto const whole{Plan.curve.length()};
std::vector<segment_data> lines{bent_slip_path(Plan, straight_path(a, b)), bent_slip_path(Plan, slip_arc(Plan.curve, a, Plan.first, side, d, 0.0, whole)), bent_slip_path(Plan, straight_path(c, d))};
if (Type != "Rkp")
lines.push_back(bent_slip_path(Plan, slip_arc(Plan.curve, c, Plan.second, -side, b, 0.0, whole)));
return lines;
}
std::vector<segment_data> editor_mode::curved_slip_reaches(curved_slip const &Plan) const
{
if (Plan.reach < Plan.half + 0.01)
return {};
auto const at = [](glm::dvec2 const &Planar) { return glm::dvec3{Planar.x, 0.0, Planar.y}; };
auto const &[a, b, c, d]{Plan.points};
return {bent_slip_path(Plan, straight_path(at(Plan.crossing - Plan.first * Plan.reach), a)), bent_slip_path(Plan, straight_path(b, at(Plan.crossing + Plan.first * Plan.reach))),
bent_slip_path(Plan, straight_path(at(Plan.crossing - Plan.second * Plan.reach), c)), bent_slip_path(Plan, straight_path(d, at(Plan.crossing + Plan.second * Plan.reach)))};
}
switch_curve::slip_report editor_mode::curved_slip_report(curved_slip const &Plan, std::string const &Type) const
{
auto const lines{curved_slip_lines(Plan, Type)};
std::vector<std::vector<switch_curve::track_point>> curves{plan_points(lines[1])};
if (lines.size() > 3)
curves.push_back(plan_points(lines[3]));
return switch_curve::check_slip({plan_points(lines[0]), plan_points(lines[2])}, curves, frame_points(m_switch.frame), Plan.middle - Plan.direction * Plan.half, Plan.middle + Plan.direction * Plan.half);
}
std::vector<std::string> editor_mode::slip_report_lines(switch_curve::slip_report const &Report)
{
// a radius counted positive the way the line turns, the other way with a minus
auto const radius = [](double const Curvature) { return std::abs(Curvature) < 2e-5 ? std::string{STR_C("straight")} : format("%.0f m", 1.0 / Curvature); };
std::vector<std::string> lines{format(STR_C("tracks through the slip R %s and %s"), radius(Report.first).c_str(), radius(Report.second).c_str())};
std::string curves;
for (auto const curvature : Report.curves)
curves += (curves.empty() ? "" : ", ") + radius(curvature);
if (false == curves.empty())
lines.push_back(format(STR_C("slip curves R %s at their sharpest"), curves.c_str()));
lines.push_back(format(STR_C("cant %.0f mm at the start, %.0f mm at the end"), Report.cant_start, Report.cant_end));
return lines;
}
void editor_mode::insert_curved_slip(int const Direction, int const Side)
{
auto &tool{m_switch};
auto const &shape{tool.templates[tool.armed]};
curved_slip plan;
if (false == plan_curved_slip(shape, Direction, Side, plan))
{
tool.status = refused(plan.problem);
return;
}
auto const report{curved_slip_report(plan, shape.type)};
std::vector<std::pair<TTrack *, editor_track::state>> states;
std::vector<TTrack *> created;
std::vector<TTrack *> removed;
std::vector<TTrack *> candidates{tool.frame_tracks};
auto &style{*tool.frame_tracks.front()};
// the curve taken out up to the joints of the template, the straights of both tracks laid out there
cut_curve(candidates, tool.frame, sample_at(tool.frame, plan.middle - plan.reach).position, sample_at(tool.frame, plan.middle + plan.reach).position, states, created, removed);
auto const parts{build_double_slip(plan.points[0], plan.points[1], plan.points[2], plan.points[3], plan.crossing, 0.0, plan.first, plan.second, plan.angle, plan.curve, style, shape.type,
[&](segment_data const &Path) { return bent_slip_path(plan, Path); })};
std::vector<TTrack *> made{parts};
for (auto const &path : curved_slip_reaches(plan))
if (auto *straight{editor_track::create_path(style, path)})
made.push_back(straight);
editor_track::commit(made);
created.insert(created.end(), made.begin(), made.end());
push_track_snapshot(std::move(states), std::move(created), std::move(removed));
straight_refresh();
switch_placed(parts);
// after what the drives placed with it tell: its radii and what the norms say of it
std::string text{STR_C("Slip bent into the curve, the ends of its crossing track free: lay on from them")};
for (auto const &line : slip_report_lines(report))
text += "\n" + line;
for (auto const &warning : report.warnings)
text += "\n" + warning;
for (auto const &note : report.notes)
text += "\n" + note;
tool.status += (tool.status.empty() ? "" : "\n") + text;
ui()->set_status(tool.status, false);
}

View File

@@ -231,7 +231,7 @@ void editor_mode::render_switch_ui()
char const *tooltip;
};
choice const choices[] = {{switch_kind::single, "Switch", "A switch in a straight or a curve"},
{switch_kind::slip, "Slip switch", "Rkp or Rkpd at a crossing of two straights at its skos"},
{switch_kind::slip, "Slip switch", "Rkp or Rkpd at a crossing of two straights at its skos, or bent into a curve of a side track"},
{switch_kind::crossing, "Crossing", "A crossing of two straights"},
{switch_kind::connection, "Connection", "Crossover, double, scissors, ladder"}};
{
@@ -277,7 +277,7 @@ void editor_mode::render_switch_ui()
std::vector<std::pair<char const *, char const *>> how;
switch (kind)
{
case switch_kind::slip: how = {{"Drag at a crossing", "of two straights at the skos of the slip: it goes there"}}; break;
case switch_kind::slip: how = {{"Drag at a crossing", "of two straights at the skos of the slip: it goes there"}, {"Drag on a curve", "bent into it, in a side track only (11.4 pkt 4b)"}}; break;
case switch_kind::crossing: how = {{"Drag at a crossing", "of two straights: it goes there"}}; break;
case switch_kind::connection:
how = {{"Drag", "from a straight onto the other track"}, {"Shift+wheel", "the kind"}, {"Ctrl+wheel", "R of the aligning curve"}};

View File

@@ -1730,6 +1730,8 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
else if (m_switch.placing && m_switch.free)
hints = m_switch.free_end.track != nullptr ? std::vector<key_hint>{{"Drag sideways", "side of the diverging track"}, {"Esc", "cancel"}}
: std::vector<key_hint>{{"Drag along", "the direction of the main track, held from 4 m on"}, {"Drag sideways", "side of the diverging track"}, {"Back to the start", "the direction free again"}, {"Esc", "cancel"}};
else if (m_switch.placing && m_switch.curved && current_switch_kind() == switch_kind::slip)
hints = {{"Drag", "the crossing track ahead through the quarter of the cursor, the slip curve of a single slip on its side"}, {"Esc", "cancel"}};
else if (m_switch.placing)
hints = m_switch.connection >= 0 && false == m_switch.curved ?
std::vector<key_hint>{{"Drag onto the other track", "the connection there"}, {"Shift+wheel", "another kind"}, {"Ctrl+wheel", "R of the aligning curve"}, {"Alt+wheel", "where a double crossover meets"}, {"Esc", "cancel"}} :
@@ -1741,7 +1743,7 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
switch (current_switch_kind())
{
case switch_kind::connection: hints = {{"Press on a straight", "and drag onto the other track"}, {"Shift+wheel", "another kind"}, {"Ctrl+wheel", "R of the aligning curve"}}; break;
case switch_kind::slip:
case switch_kind::slip: hints = {{"Press+drag at a crossing", "of two straights"}, {"Press+drag on a curve", "the slip bent into it, in a side track only (11.4 pkt 4b)"}}; break;
case switch_kind::crossing: hints = {{"Press+drag at a crossing", "of two straights"}}; break;
default: hints = {{"LMB on a track", "the straight or the curve for the switch"}, {"Press+drag on the ground", "a switch with no track, on from a free end near it"}}; break;
}
@@ -1755,7 +1757,7 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
else if (m_track_tab == track_tab::turnout && m_switch.armed < 0)
hints = {{"List in the panel", "the type of the switch"}, {"LMB on a track", "another track"}};
else if (m_track_tab == track_tab::turnout && current_switch_kind() == switch_kind::slip)
hints = {{"Press+drag at a crossing", "of two straights at the skos of the slip"}};
hints = {{"Press+drag at a crossing", "of two straights at the skos of the slip"}, {"Press+drag on a curve", "the slip bent into it, in a side track only (11.4 pkt 4b)"}};
else if (m_track_tab == track_tab::turnout)
hints = {{"Press+drag on the track", "along: direction, sideways: side"}, {"List in the panel", "the type of the switch"}, {"LMB on another track", "choose it"}};
else if (m_track_tab == track_tab::profile && m_profile.route.spans.empty())
@@ -2522,22 +2524,43 @@ void editor_mode::draw_build_overlay() const
int side;
curved_switch_pose(forward, side);
std::string snapped;
auto const station{curved_switch_start(forward, &snapped)};
auto const report{curved_switch_report(preview, station, forward)};
// a radius counted positive the way the line turns, the other way with a minus
auto const radius = [](double const Curvature) { return std::abs(Curvature) < 2e-5 ? std::string{STR_C("straight")} : format("%.0f m", 1.0 / Curvature); };
std::vector<std::string> notes;
std::vector<std::string> warnings;
std::vector<std::pair<std::string, ImU32>> lines;
if (false == snapped.empty())
lines.emplace_back(snapped, IM_COL32(255, 170, 40, 255));
if (report.curved)
double station;
if (auto const &shape{tool.templates[tool.armed]}; shape.double_slip)
{
lines.emplace_back(format(STR_C("main track R %s at the start, %s at the end"), radius(report.main_start).c_str(), radius(report.main_end).c_str()), IM_COL32(255, 255, 255, 235));
lines.emplace_back(format(STR_C("diverging track R %s at the start, %s at the end"), radius(report.branch_start).c_str(), radius(report.branch_end).c_str()), IM_COL32(255, 255, 255, 235));
// a slip bent: the radii of its tracks and its slip curves, its middle where the station is
curved_slip plan;
plan_curved_slip(shape, forward, side, plan);
snapped = plan.snapped;
station = plan.middle;
auto const report{curved_slip_report(plan, shape.type)};
for (auto const &line : slip_report_lines(report))
lines.emplace_back(line, IM_COL32(255, 255, 255, 235));
notes = report.notes;
warnings = report.warnings;
}
lines.emplace_back(format(STR_C("cant %.0f mm at the start, %.0f mm at the end"), report.cant_start, report.cant_end), IM_COL32(255, 255, 255, 235));
for (auto const &note : report.notes)
else
{
station = curved_switch_start(forward, &snapped);
auto const report{curved_switch_report(preview, station, forward)};
// a radius counted positive the way the line turns, the other way with a minus
auto const radius = [](double const Curvature) { return std::abs(Curvature) < 2e-5 ? std::string{STR_C("straight")} : format("%.0f m", 1.0 / Curvature); };
if (report.curved)
{
lines.emplace_back(format(STR_C("main track R %s at the start, %s at the end"), radius(report.main_start).c_str(), radius(report.main_end).c_str()), IM_COL32(255, 255, 255, 235));
lines.emplace_back(format(STR_C("diverging track R %s at the start, %s at the end"), radius(report.branch_start).c_str(), radius(report.branch_end).c_str()), IM_COL32(255, 255, 255, 235));
}
lines.emplace_back(format(STR_C("cant %.0f mm at the start, %.0f mm at the end"), report.cant_start, report.cant_end), IM_COL32(255, 255, 255, 235));
notes = report.notes;
warnings = report.warnings;
}
if (false == snapped.empty())
lines.emplace(lines.begin(), snapped, IM_COL32(255, 170, 40, 255));
for (auto const &note : notes)
lines.emplace_back(note, IM_COL32(170, 200, 230, 235));
for (auto const &warning : report.warnings)
for (auto const &warning : warnings)
lines.emplace_back(warning, overlay_color::invalid);
ImVec2 screen;
if (projection.project(sample_at(tool.frame, station).position, screen))

View File

@@ -12387,3 +12387,48 @@ msgstr "dwóch prostych pod skosem rozjazdu: tam stanie"
msgid "of two straights at the skos of the slip"
msgstr "dwóch prostych pod skosem rozjazdu"
msgid "The curvature of a track through the slip turns from one side to the other within it, which isn't allowed (9.7 pkt 12)"
msgstr "Krzywizna toru przez rozjazd krzyżowy zmienia stronę w jego obrębie, co jest niedopuszczalne (9.7 pkt 12)"
msgid "A slip isn't bent in the running nor in the main tracks, a bent one goes into a side track only (11.4 pkt 4b)"
msgstr "Rozjazdów krzyżowych nie łukuje się w torach szlakowych ani głównych, łukowany kładzie się tylko w torze bocznym (11.4 pkt 4b)"
msgid "A slip curve bent to R %.0f m, below the least %.0f m of a side track (table 9.2)"
msgstr "Łuk rozjazdu krzyżowego wygięty do R %.0f m, poniżej najmniejszego %.0f m w torze bocznym (tabela 9.2)"
msgid "In a side track the layouts needing no cant are recommended (9.3 pkt 5)"
msgstr "W torze bocznym zaleca się układy niewymagające przechyłki (9.3 pkt 5)"
msgid "A crossing goes only where two straights cross"
msgstr "Skrzyżowanie stawia się tylko na przecięciu dwóch prostych"
msgid "The curve of R %.0f m is as sharp as the slip curves of R %.0f m or sharper, the slip isn't bent into it"
msgstr "Łuk R %.0f m jest tak ostry jak łuki rozjazdu krzyżowego R %.0f m albo ostrzejszy, rozjazdu nie da się w niego wygiąć"
msgid "tracks through the slip R %s and %s"
msgstr "tory przez rozjazd R %s i %s"
msgid "slip curves R %s at their sharpest"
msgstr "łuki rozjazdu R %s w najostrzejszym miejscu"
msgid "Slip bent into the curve, the ends of its crossing track free: lay on from them"
msgstr "Rozjazd krzyżowy wygięty w łuk, końce toru przecinającego wolne: kładź od nich dalej"
msgid "the crossing track ahead through the quarter of the cursor, the slip curve of a single slip on its side"
msgstr "tor przecinający w przód przez ćwiartkę kursora, łuk rozjazdu pojedynczego po jej stronie"
msgid "Press+drag on a curve"
msgstr "Wciśnij i przeciągnij na łuku"
msgid "the slip bent into it, in a side track only (11.4 pkt 4b)"
msgstr "rozjazd krzyżowy wygięty w łuk, tylko w torze bocznym (11.4 pkt 4b)"
msgid "Drag on a curve"
msgstr "Przeciągnij na łuku"
msgid "bent into it, in a side track only (11.4 pkt 4b)"
msgstr "wygięty w łuk, tylko w torze bocznym (11.4 pkt 4b)"
msgid "Rkp or Rkpd at a crossing of two straights at its skos, or bent into a curve of a side track"
msgstr "Rozjazd krzyżowy pojedynczy (Rkp) albo podwójny (Rkpd) na skrzyżowaniu dwóch prostych pod jego skosem albo wygięty w łuk toru bocznego"