mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-04 13:45:49 +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:
@@ -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;
|
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 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);
|
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);
|
void insert_curved_switch(int const Direction, int const Side);
|
||||||
// a crossover between two tracks running side by side along a curve (editormodeconnectioncurve.cpp)
|
// a crossover between two tracks running side by side along a curve (editormodeconnectioncurve.cpp)
|
||||||
struct curve_mapping;
|
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;
|
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 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);
|
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);
|
bool start_extend(editor_track::snap_target const &End);
|
||||||
void finish_extend();
|
void finish_extend();
|
||||||
std::vector<segment_data> extend_pieces() const;
|
std::vector<segment_data> extend_pieces() const;
|
||||||
|
|||||||
@@ -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));
|
text += format(", drive %.3f m from the joint", geometry::plan_distance(found->standing.location, start));
|
||||||
else
|
else
|
||||||
text += ", no drive";
|
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);
|
WriteLog(text);
|
||||||
}
|
}
|
||||||
return selftest_result::handled;
|
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.frame_tracks = chain.tracks;
|
||||||
tool.curved = true;
|
tool.curved = true;
|
||||||
tool.along = nearest_station(tool.frame, point);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,6 +59,16 @@ std::vector<segment_data> editor_mode::switch_preview() const
|
|||||||
int forward;
|
int forward;
|
||||||
int side;
|
int side;
|
||||||
curved_switch_pose(forward, 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);
|
return curved_switch_paths(tool.templates[tool.armed], tool.frame, curved_switch_start(forward), forward, side);
|
||||||
}
|
}
|
||||||
if (tool.templates[tool.armed].double_slip)
|
if (tool.templates[tool.armed].double_slip)
|
||||||
@@ -104,9 +114,10 @@ bool editor_mode::start_switch_placement()
|
|||||||
if (current_straight().tracks.empty())
|
if (current_straight().tracks.empty())
|
||||||
{
|
{
|
||||||
auto *track{selected};
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
editor_track::curve curve;
|
editor_track::curve curve;
|
||||||
@@ -216,6 +227,11 @@ void editor_mode::finish_switch_placement()
|
|||||||
int forward;
|
int forward;
|
||||||
int side;
|
int side;
|
||||||
curved_switch_pose(forward, 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);
|
tool.along = curved_switch_start(forward);
|
||||||
insert_curved_switch(forward, side);
|
insert_curved_switch(forward, side);
|
||||||
return;
|
return;
|
||||||
@@ -832,83 +848,97 @@ std::vector<TTrack *> editor_mode::link_tracks(editor_track::snap_target const &
|
|||||||
return pieces;
|
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;
|
segment_data path;
|
||||||
path.points[segment_data::point::start] = Start;
|
path.points[segment_data::point::start] = place(Begin);
|
||||||
path.points[segment_data::point::end] = End;
|
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;
|
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
|
// 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 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 gap{std::max(0.25, 0.06 / halfsine)};
|
||||||
auto const side{cross(First, Second) > 0.0 ? 1.0 : -1.0};
|
auto const side{cross(First, Second) > 0.0 ? 1.0 : -1.0};
|
||||||
auto const whole{Curve.length()};
|
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;
|
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); };
|
||||||
// where it changes the radius made of pieces, with one curve from end to end for the versions which know no pieces
|
// laid out on a straight, bent where it goes into a curve
|
||||||
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 laid = [&](segment_data const &Path) { return Bend ? Bend(Path) : Path; };
|
||||||
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 back = [](segment_data const &Path) {
|
auto const back = [](segment_data const &Path) {
|
||||||
auto result{reversed(Path)};
|
auto result{reversed(Path)};
|
||||||
std::reverse(result.pieces.begin(), result.pieces.end());
|
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")
|
if (Type == "Skrz")
|
||||||
{
|
{
|
||||||
// the two tracks just cross, their ends far from the crossing
|
// 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, laid(straight(PointA, PointB))));
|
||||||
parts.push_back(editor_track::create_path(Style, straight(PointC, PointD)));
|
parts.push_back(editor_track::create_path(Style, laid(straight(PointC, PointD))));
|
||||||
parts.erase(std::remove(parts.begin(), parts.end(), nullptr), parts.end());
|
parts.erase(std::remove(parts.begin(), parts.end(), nullptr), parts.end());
|
||||||
record_slip(parts, "crossing", Type, 0.0, Angle, base);
|
record_slip(parts, "crossing", Type, 0.0, Angle, base);
|
||||||
return parts;
|
return parts;
|
||||||
@@ -944,21 +974,21 @@ std::vector<TTrack *> editor_mode::build_double_slip(glm::dvec3 const &PointA, g
|
|||||||
editor_track::switch_template shape;
|
editor_track::switch_template shape;
|
||||||
shape.length = glm::distance(plan_of(PointA), Crossing);
|
shape.length = glm::distance(plan_of(PointA), Crossing);
|
||||||
bool const single{Type == "Rkp"};
|
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)
|
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
|
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)
|
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
|
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, {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, {straight(PointD, centreD), back(arc(PointA, First, side, PointD, middle + split, whole))}, Style, base + "_d"));
|
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, straight(centreA, centreB)));
|
parts.push_back(editor_track::create_path(Style, laid(straight(centreA, centreB))));
|
||||||
parts.push_back(editor_track::create_path(Style, straight(centreC, centreD)));
|
parts.push_back(editor_track::create_path(Style, laid(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_path(Style, laid(arc(PointA, First, side, PointD, middle - split, middle + split))));
|
||||||
if (false == single)
|
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());
|
parts.erase(std::remove(parts.begin(), parts.end(), nullptr), parts.end());
|
||||||
record_slip(parts, "slip", Type, Curve.radius, Angle, base, Curve.middle);
|
record_slip(parts, "slip", Type, Curve.radius, Angle, base, Curve.middle);
|
||||||
return parts;
|
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")
|
if (Shape.type == "Rkpd")
|
||||||
{
|
{
|
||||||
TTrack *slip{nullptr};
|
TTrack *slip{nullptr};
|
||||||
double closest{25.0};
|
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 *section : simulation::Region->sections(Point, 40.f))
|
||||||
for (auto const &cell : section->m_cells)
|
for (auto const &cell : section->m_cells)
|
||||||
for (auto *track : cell.m_paths)
|
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));
|
closest = glm::distance(plan_of(track->location()), plan_of(Point));
|
||||||
slip = track;
|
slip = track;
|
||||||
@@ -1486,53 +1521,60 @@ double editor_mode::curved_switch_start(int const Direction, std::string *What)
|
|||||||
return start;
|
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
|
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};
|
auto const &tool{m_switch};
|
||||||
if (Paths.size() < 2 || tool.armed < 0 || tool.armed >= static_cast<int>(tool.templates.size()))
|
if (Paths.size() < 2 || tool.armed < 0 || tool.armed >= static_cast<int>(tool.templates.size()))
|
||||||
return {};
|
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]};
|
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 map = [&](glm::dvec3 const &Point, glm::dvec2 &Tangent, glm::dvec2 const &Localtangent) {
|
||||||
auto const sample{sample_at(Frame, Station + Direction * Local.z)};
|
auto const sample{sample_at(Frame, Station + Direction * Point.z)};
|
||||||
auto const along{sample.tangent * static_cast<double>(Direction)};
|
auto const along{sample.tangent * static_cast<double>(Direction)};
|
||||||
glm::dvec2 const across{glm::dvec2{-along.y, along.x} * static_cast<double>(Side)};
|
glm::dvec2 const across{glm::dvec2{-along.y, along.x} * static_cast<double>(Side)};
|
||||||
Tangent = glm::normalize(along * Localtangent.y + across * Localtangent.x);
|
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 cant{glm::radians(static_cast<double>(sample.roll) * Direction)};
|
||||||
auto const cone{-Local.x * Side * std::tan(cant)};
|
auto const cone{-Point.x * Side * std::tan(cant)};
|
||||||
return glm::dvec3{planar.x, sample.position.y + Local.y + cone, planar.y};
|
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 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};
|
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};
|
path.points[segment_data::point::control2] = {-Enddirection.x * handle, -(End.y - Start.y) / 3.0, -Enddirection.y * handle};
|
||||||
return path;
|
return path;
|
||||||
};
|
};
|
||||||
auto const bend = [&](segment_data const &Local) {
|
auto const &start{Local.points[segment_data::point::start]};
|
||||||
auto const &start{Local.points[segment_data::point::start]};
|
auto const &end{Local.points[segment_data::point::end]};
|
||||||
auto const &end{Local.points[segment_data::point::end]};
|
auto const &control1{Local.points[segment_data::point::control1]};
|
||||||
auto const &control1{Local.points[segment_data::point::control1]};
|
auto const &control2{Local.points[segment_data::point::control2]};
|
||||||
auto const &control2{Local.points[segment_data::point::control2]};
|
glm::dvec2 const chord{end.x - start.x, end.z - start.z};
|
||||||
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 localstart{glm::normalize(control1 != glm::dvec3{} ? plan_of(control1) : chord)};
|
auto const localend{glm::normalize(control2 != glm::dvec3{} ? -plan_of(control2) : chord)};
|
||||||
auto const localend{glm::normalize(control2 != glm::dvec3{} ? -plan_of(control2) : chord)};
|
glm::dvec2 startdirection;
|
||||||
glm::dvec2 startdirection;
|
glm::dvec2 enddirection;
|
||||||
glm::dvec2 enddirection;
|
auto const p0{map(start, startdirection, localstart)};
|
||||||
auto const p0{map(start, startdirection, localstart)};
|
auto const p3{map(end, enddirection, localend)};
|
||||||
auto const p3{map(end, enddirection, localend)};
|
auto path{hermite(p0, startdirection, p3, enddirection)};
|
||||||
auto path{hermite(p0, startdirection, p3, enddirection)};
|
{
|
||||||
{
|
auto const cone = [&](double const T) {
|
||||||
auto const cone = [&](double const T) {
|
auto const point{bezier{path}.point(T)};
|
||||||
auto const point{bezier{path}.point(T)};
|
auto const sample{sample_at(Frame, nearest_station(Frame, point))};
|
||||||
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})};
|
||||||
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)));
|
||||||
return sample.position.y - lateral * std::tan(glm::radians(static_cast<double>(sample.roll)));
|
};
|
||||||
};
|
auto const h1{cone(1.0 / 3.0)};
|
||||||
auto const h1{cone(1.0 / 3.0)};
|
auto const h2{cone(2.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 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};
|
||||||
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::control1].y = y1 - p0.y;
|
path.points[segment_data::point::control2].y = y2 - p3.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 rollstart{sample_at(Frame, Station + Direction * start.z).roll * Direction};
|
auto const rollend{sample_at(Frame, Station + Direction * end.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.rolls = {static_cast<float>(rollstart), static_cast<float>(rollend)};
|
path.radius = Local.radius;
|
||||||
path.radius = Local.radius;
|
{
|
||||||
{
|
bezier const curve{path};
|
||||||
bezier const curve{path};
|
auto const begin{plan_of(curve.first(0.0))};
|
||||||
auto const begin{plan_of(curve.first(0.0))};
|
auto const finish{plan_of(curve.first(1.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};
|
||||||
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)
|
||||||
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)));
|
||||||
path.radius = static_cast<float>(std::copysign(curve.plan_length() / std::abs(turn), static_cast<double>(Local.radius)));
|
}
|
||||||
}
|
return path;
|
||||||
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()};
|
std::vector<segment_data> locals{Shape.local.begin(), Shape.local.end()};
|
||||||
if (Shape.twofold())
|
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)
|
for (std::size_t i = 0; i < locals.size(); ++i)
|
||||||
{
|
{
|
||||||
auto const &local{locals[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)
|
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
|
// the main tracks follow the curve they are laid into
|
||||||
if (i % 2 == 0)
|
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;
|
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();
|
straight_refresh();
|
||||||
switch_placed(placed);
|
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 ¬e : report.notes)
|
||||||
|
text += "\n" + note;
|
||||||
|
tool.status += (tool.status.empty() ? "" : "\n") + text;
|
||||||
|
ui()->set_status(tool.status, false);
|
||||||
|
}
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ void editor_mode::render_switch_ui()
|
|||||||
char const *tooltip;
|
char const *tooltip;
|
||||||
};
|
};
|
||||||
choice const choices[] = {{switch_kind::single, "Switch", "A switch in a straight or a curve"},
|
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::crossing, "Crossing", "A crossing of two straights"},
|
||||||
{switch_kind::connection, "Connection", "Crossover, double, scissors, ladder"}};
|
{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;
|
std::vector<std::pair<char const *, char const *>> how;
|
||||||
switch (kind)
|
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::crossing: how = {{"Drag at a crossing", "of two straights: it goes there"}}; break;
|
||||||
case switch_kind::connection:
|
case switch_kind::connection:
|
||||||
how = {{"Drag", "from a straight onto the other track"}, {"Shift+wheel", "the kind"}, {"Ctrl+wheel", "R of the aligning curve"}};
|
how = {{"Drag", "from a straight onto the other track"}, {"Shift+wheel", "the kind"}, {"Ctrl+wheel", "R of the aligning curve"}};
|
||||||
|
|||||||
@@ -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)
|
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"}}
|
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"}};
|
: 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)
|
else if (m_switch.placing)
|
||||||
hints = m_switch.connection >= 0 && false == m_switch.curved ?
|
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"}} :
|
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())
|
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::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;
|
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;
|
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)
|
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"}};
|
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)
|
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)
|
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"}};
|
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())
|
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;
|
int side;
|
||||||
curved_switch_pose(forward, side);
|
curved_switch_pose(forward, side);
|
||||||
std::string snapped;
|
std::string snapped;
|
||||||
auto const station{curved_switch_start(forward, &snapped)};
|
std::vector<std::string> notes;
|
||||||
auto const report{curved_switch_report(preview, station, forward)};
|
std::vector<std::string> warnings;
|
||||||
// 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::pair<std::string, ImU32>> lines;
|
std::vector<std::pair<std::string, ImU32>> lines;
|
||||||
if (false == snapped.empty())
|
double station;
|
||||||
lines.emplace_back(snapped, IM_COL32(255, 170, 40, 255));
|
if (auto const &shape{tool.templates[tool.armed]}; shape.double_slip)
|
||||||
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));
|
// a slip bent: the radii of its tracks and its slip curves, its middle where the station is
|
||||||
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));
|
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));
|
else
|
||||||
for (auto const ¬e : report.notes)
|
{
|
||||||
|
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 ¬e : notes)
|
||||||
lines.emplace_back(note, IM_COL32(170, 200, 230, 235));
|
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);
|
lines.emplace_back(warning, overlay_color::invalid);
|
||||||
ImVec2 screen;
|
ImVec2 screen;
|
||||||
if (projection.project(sample_at(tool.frame, station).position, screen))
|
if (projection.project(sample_at(tool.frame, station).position, screen))
|
||||||
|
|||||||
@@ -12387,3 +12387,48 @@ msgstr "dwóch prostych pod skosem rozjazdu: tam stanie"
|
|||||||
|
|
||||||
msgid "of two straights at the skos of the slip"
|
msgid "of two straights at the skos of the slip"
|
||||||
msgstr "dwóch prostych pod skosem rozjazdu"
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user