mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-03 15:30:21 +01:00
fix(editor): a slip goes to the crossing under the cursor or nowhere, shown before the click
A slip went to the crossing of the selected straight with another straight within 40 m of the click. When the track under the cursor wasn't taken as a straight (a slight curve, a switch), the slip silently went to another crossing up to 40 m away. A double slip within 25 m of the click was replaced, even when the click was on a free crossing. - The crossing nearest the cursor, within 12 m, is found among all the rail tracks. A slip goes only there; when that track is a curve or a switch, the tool says so. - A double slip is replaced only when it is nearer the cursor than any crossing. - The cursor is taken at the height of the straight under it, so on a grade the 3D view doesn't put the click metres away from the track. - Before the click, the view marks the crossing the slip goes to, with its two straights, or the reason it can't go there.
This commit is contained in:
@@ -1432,6 +1432,26 @@ class editor_mode : public application_mode, private editor_track::observer
|
|||||||
bool stretchable(editor_track::snap_target const &Target) const;
|
bool stretchable(editor_track::snap_target const &Target) const;
|
||||||
std::vector<TTrack *> link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, TTrack const &Style, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created);
|
std::vector<TTrack *> link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, TTrack const &Style, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created);
|
||||||
bool cut_straight(editor_track::straight const &Line, double const From, double const To, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created, std::vector<TTrack *> &Removed, TTrack **Style);
|
bool cut_straight(editor_track::straight const &Line, double const From, double const To, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created, std::vector<TTrack *> &Removed, TTrack **Style);
|
||||||
|
// where a slip or a crossing goes for the cursor at Point on the straight Line: the crossing of Line with the rail track
|
||||||
|
// nearest the cursor, or the double slip there to be replaced
|
||||||
|
struct slip_plan
|
||||||
|
{
|
||||||
|
std::string problem;
|
||||||
|
std::string note;
|
||||||
|
TTrack *replaces{nullptr};
|
||||||
|
TTrack *other{nullptr};
|
||||||
|
glm::dvec2 crossing{0.0};
|
||||||
|
glm::dvec2 first{0.0, 1.0};
|
||||||
|
glm::dvec2 direction{0.0, 1.0};
|
||||||
|
double angle{0.0};
|
||||||
|
double alongfirst{0.0};
|
||||||
|
double half{0.0};
|
||||||
|
editor_track::slip_curve curve;
|
||||||
|
std::array<glm::dvec3, 4> points{};
|
||||||
|
};
|
||||||
|
bool plan_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape, slip_plan &Plan) const;
|
||||||
|
// the cursor where it points at the straight, at the height of the straight there
|
||||||
|
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");
|
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");
|
||||||
|
|||||||
@@ -124,18 +124,19 @@ bool editor_mode::start_switch_placement()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto const &line{current_straight()};
|
auto const &line{current_straight()};
|
||||||
auto const along{std::clamp(line.along(ground), 0.0, line.length)};
|
auto const cursor{cursor_on_straight(line)};
|
||||||
|
auto const along{std::clamp(line.along(cursor), 0.0, line.length)};
|
||||||
auto const point{line.start + glm::dvec3{line.direction.x, line.grade, line.direction.y} * along};
|
auto const point{line.start + glm::dvec3{line.direction.x, line.grade, line.direction.y} * along};
|
||||||
if (plan_distance(point, ground) > kSwitchReach)
|
if (plan_distance(point, cursor) > kSwitchReach)
|
||||||
return false;
|
return false;
|
||||||
if (tool.templates[tool.armed].double_slip)
|
if (tool.templates[tool.armed].double_slip)
|
||||||
{
|
{
|
||||||
insert_double_slip(line, ground, tool.templates[tool.armed]);
|
insert_double_slip(line, cursor, tool.templates[tool.armed]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
tool.along = along;
|
tool.along = along;
|
||||||
tool.point = point;
|
tool.point = point;
|
||||||
tool.mouse = ground;
|
tool.mouse = cursor;
|
||||||
tool.line = line;
|
tool.line = line;
|
||||||
tool.placing = true;
|
tool.placing = true;
|
||||||
return true;
|
return true;
|
||||||
@@ -817,61 +818,99 @@ std::vector<TTrack *> editor_mode::build_double_slip(glm::dvec3 const &PointA, g
|
|||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool editor_mode::insert_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape)
|
glm::dvec3 editor_mode::cursor_on_straight(editor_track::straight const &Line) const
|
||||||
{
|
{
|
||||||
auto &state{m_straights};
|
// the height of the straight under the cursor, worked out anew till it settles: on a grade the plane of the start of the
|
||||||
state.status.clear();
|
// straight would put the cursor metres away from where it points at the track
|
||||||
{
|
auto cursor{cursor_level(Line.start.y)};
|
||||||
auto const sections{simulation::Region->sections(Point, 40.f)};
|
for (int i = 0; i < 4; ++i)
|
||||||
for (auto *section : sections)
|
cursor = cursor_level(Line.start.y + Line.grade * std::clamp(Line.along(cursor), 0.0, Line.length));
|
||||||
for (auto const &cell : section->m_cells)
|
return cursor;
|
||||||
for (auto *track : cell.m_paths)
|
}
|
||||||
if (Shape.type == "Rkpd" && track->eType == tt_Switch && false == track->m_editorremoved && track->DoubleSlip() && glm::distance(plan_of(track->location()), plan_of(Point)) < 25.0)
|
|
||||||
return replace_double_slip(*track, Shape);
|
bool editor_mode::plan_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape, slip_plan &Plan) const
|
||||||
}
|
{
|
||||||
TTrack *other{nullptr};
|
Plan = slip_plan{};
|
||||||
|
// the crossing of the straight with the rail track nearest the cursor, whatever that track is: the slip goes there or
|
||||||
|
// nowhere, never to another crossing around
|
||||||
|
TTrack *nearest{nullptr};
|
||||||
glm::dvec2 crossing{0.0};
|
glm::dvec2 crossing{0.0};
|
||||||
double best{40.0};
|
double best{kSlipReach};
|
||||||
auto const sections{simulation::Region->sections(Point, 60.f)};
|
auto const category{Line.tracks.empty() ? 1 : Line.tracks.front()->iCategoryFlag & 15};
|
||||||
for (auto *section : sections)
|
for (auto *section : simulation::Region->sections(Point, 300.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 (std::find(Line.tracks.begin(), Line.tracks.end(), track) != Line.tracks.end() || track->eType != tt_Normal || track->m_editorremoved || false == editor_track::is_straight(*track, m_straights.tolerance))
|
if (track == nullptr || track->m_editorremoved || (track->iCategoryFlag & 15) != category || std::find(Line.tracks.begin(), Line.tracks.end(), track) != Line.tracks.end())
|
||||||
continue;
|
continue;
|
||||||
auto const &path{track->m_paths.front()};
|
for (auto const &path : track->m_paths)
|
||||||
auto const a{plan_of(path.points[segment_data::point::start])};
|
|
||||||
auto const b{plan_of(path.points[segment_data::point::end])};
|
|
||||||
auto const segment{b - a};
|
|
||||||
auto const determinant{cross(Line.direction, segment)};
|
|
||||||
if (std::abs(determinant) < 1e-9)
|
|
||||||
continue;
|
|
||||||
auto const offset{a - plan_of(Line.start)};
|
|
||||||
auto const along{cross(offset, segment) / determinant};
|
|
||||||
auto const fraction{cross(offset, Line.direction) / determinant};
|
|
||||||
if (fraction < -0.01 || fraction > 1.01 || along < 0.0 || along > Line.length)
|
|
||||||
continue;
|
|
||||||
auto const position{plan_of(Line.start) + Line.direction * along};
|
|
||||||
auto const distance{glm::distance(position, plan_of(Point))};
|
|
||||||
if (distance < best)
|
|
||||||
{
|
{
|
||||||
best = distance;
|
bezier const curve{path};
|
||||||
other = track;
|
auto const straight{path.points[segment_data::point::control1] == glm::dvec3{} && path.points[segment_data::point::control2] == glm::dvec3{}};
|
||||||
crossing = position;
|
int const pieces{straight ? 1 : 16};
|
||||||
|
auto previous{plan_of(path.points[segment_data::point::start])};
|
||||||
|
for (int i = 1; i <= pieces; ++i)
|
||||||
|
{
|
||||||
|
auto const next{straight ? plan_of(path.points[segment_data::point::end]) : plan_of(curve.point(static_cast<double>(i) / pieces))};
|
||||||
|
auto const segment{next - previous};
|
||||||
|
auto const determinant{cross(Line.direction, segment)};
|
||||||
|
if (std::abs(determinant) > 1e-9)
|
||||||
|
{
|
||||||
|
auto const offset{previous - plan_of(Line.start)};
|
||||||
|
auto const along{cross(offset, segment) / determinant};
|
||||||
|
auto const fraction{cross(offset, Line.direction) / determinant};
|
||||||
|
auto const position{plan_of(Line.start) + Line.direction * along};
|
||||||
|
if (fraction >= -0.01 && fraction <= 1.01 && along >= 0.0 && along <= Line.length && glm::distance(position, plan_of(Point)) < best)
|
||||||
|
{
|
||||||
|
best = glm::distance(position, plan_of(Point));
|
||||||
|
nearest = track;
|
||||||
|
crossing = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previous = next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// a double slip nearer the cursor than any crossing is replaced by the one chosen
|
||||||
|
if (Shape.type == "Rkpd")
|
||||||
|
{
|
||||||
|
TTrack *slip{nullptr};
|
||||||
|
double closest{25.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)
|
||||||
|
{
|
||||||
|
closest = glm::distance(plan_of(track->location()), plan_of(Point));
|
||||||
|
slip = track;
|
||||||
|
}
|
||||||
|
if (slip != nullptr && (nearest == nullptr || closest < best))
|
||||||
|
{
|
||||||
|
Plan.replaces = slip;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (other == nullptr)
|
if (nearest == nullptr)
|
||||||
{
|
{
|
||||||
state.status = STR_C("No straight crossing the selected one near the click");
|
Plan.problem = STR_C("No track crosses the selected straight under the cursor");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto const second{editor_track::find_straight(*other, m_straights.tolerance)};
|
if (nearest->eType != tt_Normal)
|
||||||
if (second.tracks.empty())
|
{
|
||||||
|
Plan.problem = STR_C("The track crossing here is a switch: a slip goes only where two straights cross");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (false == editor_track::is_straight(*nearest, m_straights.tolerance))
|
||||||
|
{
|
||||||
|
Plan.problem = STR_C("The track crossing here isn't straight: a slip goes only where two straights cross");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto const second{editor_track::find_straight(*nearest, m_straights.tolerance)};
|
||||||
|
if (second.tracks.empty())
|
||||||
|
{
|
||||||
|
Plan.problem = STR_C("The track crossing here isn't straight: a slip goes only where two straights cross");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
auto const first{Line.direction};
|
auto const first{Line.direction};
|
||||||
auto direction{second.direction};
|
auto direction{second.direction};
|
||||||
if (glm::dot(first, direction) < 0.0)
|
if (glm::dot(first, direction) < 0.0)
|
||||||
@@ -880,7 +919,7 @@ bool editor_mode::insert_double_slip(editor_track::straight const &Line, glm::dv
|
|||||||
bool const plain{Shape.type == "Skrz"};
|
bool const plain{Shape.type == "Skrz"};
|
||||||
if (angle < glm::radians(0.5) || angle > glm::radians(plain ? 89.0 : 30.0))
|
if (angle < glm::radians(0.5) || angle > glm::radians(plain ? 89.0 : 30.0))
|
||||||
{
|
{
|
||||||
state.status = STR_C("The straights cross at an angle unsuitable for a double slip");
|
Plan.problem = STR_C("The straights cross at an angle unsuitable for a double slip");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto const alongfirst{glm::dot(crossing - plan_of(Line.start), Line.direction)};
|
auto const alongfirst{glm::dot(crossing - plan_of(Line.start), Line.direction)};
|
||||||
@@ -896,7 +935,7 @@ bool editor_mode::insert_double_slip(editor_track::straight const &Line, glm::dv
|
|||||||
half = available;
|
half = available;
|
||||||
if (half < 2.0)
|
if (half < 2.0)
|
||||||
{
|
{
|
||||||
state.status = STR_C("Not enough straight track around the crossing");
|
Plan.problem = STR_C("Not enough straight track around the crossing");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -906,20 +945,54 @@ bool editor_mode::insert_double_slip(editor_track::straight const &Line, glm::dv
|
|||||||
half = available;
|
half = available;
|
||||||
if (curve.radius < 50.0)
|
if (curve.radius < 50.0)
|
||||||
{
|
{
|
||||||
state.status = STR_C("Not enough straight track around the crossing");
|
Plan.problem = STR_C("Not enough straight track around the crossing");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
state.status = format(STR_C("R reduced to %d m, the straights end %d m from the crossing"), static_cast<int>(curve.radius), static_cast<int>(available));
|
Plan.note = format(STR_C("R reduced to %d m, the straights end %d m from the crossing"), static_cast<int>(curve.radius), static_cast<int>(available));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const heightfirst = [&](double const Along) { return Line.start.y + Line.grade * Along; };
|
auto const heightfirst = [&](double const Along) { return Line.start.y + Line.grade * Along; };
|
||||||
auto const heightsecond = [&](double const Along) { return second.start.y + second.grade * Along; };
|
auto const heightsecond = [&](double const Along) { return second.start.y + second.grade * Along; };
|
||||||
auto const sign{glm::dot(second.direction, direction) > 0.0 ? 1.0 : -1.0};
|
auto const sign{glm::dot(second.direction, direction) > 0.0 ? 1.0 : -1.0};
|
||||||
auto const at = [&](glm::dvec2 const &Planar, double const Height) { return glm::dvec3{Planar.x, Height, Planar.y}; };
|
auto const at = [&](glm::dvec2 const &Planar, double const Height) { return glm::dvec3{Planar.x, Height, Planar.y}; };
|
||||||
auto const pointA{at(crossing - first * half, heightfirst(alongfirst - half))};
|
Plan.other = nearest;
|
||||||
auto const pointB{at(crossing + first * half, heightfirst(alongfirst + half))};
|
Plan.crossing = crossing;
|
||||||
auto const pointC{at(crossing - direction * half, heightsecond(alongsecond - sign * half))};
|
Plan.first = first;
|
||||||
auto const pointD{at(crossing + direction * half, heightsecond(alongsecond + sign * half))};
|
Plan.direction = direction;
|
||||||
|
Plan.angle = angle;
|
||||||
|
Plan.alongfirst = alongfirst;
|
||||||
|
Plan.half = half;
|
||||||
|
Plan.curve = curve;
|
||||||
|
Plan.points = {at(crossing - first * half, heightfirst(alongfirst - half)), at(crossing + first * half, heightfirst(alongfirst + half)), at(crossing - direction * half, heightsecond(alongsecond - sign * half)),
|
||||||
|
at(crossing + direction * half, heightsecond(alongsecond + sign * half))};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool editor_mode::insert_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape)
|
||||||
|
{
|
||||||
|
auto &state{m_straights};
|
||||||
|
state.status.clear();
|
||||||
|
slip_plan plan;
|
||||||
|
if (false == plan_double_slip(Line, Point, Shape, plan))
|
||||||
|
{
|
||||||
|
state.status = plan.problem;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (plan.replaces != nullptr)
|
||||||
|
return replace_double_slip(*plan.replaces, Shape);
|
||||||
|
state.status = plan.note;
|
||||||
|
auto *other{plan.other};
|
||||||
|
auto const crossing{plan.crossing};
|
||||||
|
auto const first{plan.first};
|
||||||
|
auto const direction{plan.direction};
|
||||||
|
auto const angle{plan.angle};
|
||||||
|
auto const alongfirst{plan.alongfirst};
|
||||||
|
auto const half{plan.half};
|
||||||
|
auto const curve{plan.curve};
|
||||||
|
auto const heightfirst = [&](double const Along) { return Line.start.y + Line.grade * Along; };
|
||||||
|
auto const &pointA{plan.points[0]};
|
||||||
|
auto const &pointB{plan.points[1]};
|
||||||
|
auto const &pointC{plan.points[2]};
|
||||||
|
auto const &pointD{plan.points[3]};
|
||||||
|
|
||||||
std::vector<std::pair<TTrack *, editor_track::state>> states;
|
std::vector<std::pair<TTrack *, editor_track::state>> states;
|
||||||
std::vector<TTrack *> created;
|
std::vector<TTrack *> created;
|
||||||
|
|||||||
@@ -2107,6 +2107,48 @@ void editor_mode::draw_build_overlay() const
|
|||||||
drawlist->AddText(ImVec2(screen.x + 18.0f, screen.y + 12.0f), valid ? (plan.warning.empty() ? overlay_color::highlight : overlay_color::warning) : overlay_color::invalid, text.c_str());
|
drawlist->AddText(ImVec2(screen.x + 18.0f, screen.y + 12.0f), valid ? (plan.warning.empty() ? overlay_color::highlight : overlay_color::warning) : overlay_color::invalid, text.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto const *selected{selected_track()}; false == m_switch.placing && m_track_tab == track_tab::turnout && m_switch.connection < 0 && m_switch.armed >= 0 && m_switch.armed < static_cast<int>(m_switch.templates.size()) &&
|
||||||
|
m_switch.templates[m_switch.armed].double_slip && selected != nullptr && selected == m_straights.current_for && false == m_straights.current.tracks.empty() &&
|
||||||
|
false == ImGui::GetIO().WantCaptureMouse)
|
||||||
|
{
|
||||||
|
// where the slip goes for a click there, before the click
|
||||||
|
auto const &line{m_straights.current};
|
||||||
|
auto const cursor{cursor_on_straight(line)};
|
||||||
|
auto const along{std::clamp(line.along(cursor), 0.0, line.length)};
|
||||||
|
if (plan_distance(line.start + glm::dvec3{line.direction.x, line.grade, line.direction.y} * along, cursor) <= kSwitchReach)
|
||||||
|
{
|
||||||
|
slip_plan plan;
|
||||||
|
auto const valid{plan_double_slip(line, cursor, m_switch.templates[m_switch.armed], plan)};
|
||||||
|
std::string text{plan.problem};
|
||||||
|
glm::dvec3 marker{cursor};
|
||||||
|
ImU32 colour{overlay_color::invalid};
|
||||||
|
if (valid && plan.replaces != nullptr)
|
||||||
|
{
|
||||||
|
marker = plan.replaces->location();
|
||||||
|
colour = overlay_color::highlight;
|
||||||
|
text = STR_C("Click: this double slip is replaced by the one chosen");
|
||||||
|
}
|
||||||
|
else if (valid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i += 2)
|
||||||
|
{
|
||||||
|
segment_data path;
|
||||||
|
path.points[segment_data::point::start] = plan.points[i];
|
||||||
|
path.points[segment_data::point::end] = plan.points[i + 1];
|
||||||
|
drawpath(path, overlay_color::marked);
|
||||||
|
}
|
||||||
|
marker = {plan.crossing.x, line.start.y + line.grade * plan.alongfirst, plan.crossing.y};
|
||||||
|
colour = overlay_color::highlight;
|
||||||
|
text = plan.note.empty() ? std::string{STR_C("Click: it goes here")} : plan.note;
|
||||||
|
}
|
||||||
|
ImVec2 screen;
|
||||||
|
if (projection.project(marker, screen))
|
||||||
|
{
|
||||||
|
drawlist->AddCircle(screen, 12.0f, colour, 20, 2.5f);
|
||||||
|
drawlist->AddText({screen.x + 16.0f, screen.y - 8.0f}, colour, text.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (m_switch.placing)
|
else if (m_switch.placing)
|
||||||
{
|
{
|
||||||
for (auto const &piece : switch_preview())
|
for (auto const &piece : switch_preview())
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ float const kHandleRadius{10.0f};
|
|||||||
double const kLaySnapRadius{5.0};
|
double const kLaySnapRadius{5.0};
|
||||||
float const kSnapPixels{16.0f};
|
float const kSnapPixels{16.0f};
|
||||||
double const kSwitchReach{6.0}; // a press farther from the selected track selects another one instead of placing the switch
|
double const kSwitchReach{6.0}; // a press farther from the selected track selects another one instead of placing the switch
|
||||||
|
double const kSlipReach{12.0}; // m; a slip goes to the crossing nearest the cursor, if it's this close
|
||||||
double const kLineReach{2000.0}; // how far each way the Curve tab follows the line
|
double const kLineReach{2000.0}; // how far each way the Curve tab follows the line
|
||||||
double const kDigitizeFit{0.5};
|
double const kDigitizeFit{0.5};
|
||||||
double const kDrawnReach{100000.0};
|
double const kDrawnReach{100000.0};
|
||||||
|
|||||||
@@ -9919,3 +9919,18 @@ msgstr "Tor leży wyżej lub niżej, niż według NMT jest grunt, o więcej niż
|
|||||||
|
|
||||||
msgid "Against the terrain model"
|
msgid "Against the terrain model"
|
||||||
msgstr "Także względem NMT"
|
msgstr "Także względem NMT"
|
||||||
|
|
||||||
|
msgid "No track crosses the selected straight under the cursor"
|
||||||
|
msgstr "Pod kursorem żaden tor nie przecina zaznaczonej prostej"
|
||||||
|
|
||||||
|
msgid "The track crossing here is a switch: a slip goes only where two straights cross"
|
||||||
|
msgstr "Tor, który tu przecina, jest rozjazdem: rozjazd krzyżowy stawia się tylko na przecięciu dwóch prostych"
|
||||||
|
|
||||||
|
msgid "The track crossing here isn't straight: a slip goes only where two straights cross"
|
||||||
|
msgstr "Tor, który tu przecina, nie jest prosty: rozjazd krzyżowy stawia się tylko na przecięciu dwóch prostych"
|
||||||
|
|
||||||
|
msgid "Click: this double slip is replaced by the one chosen"
|
||||||
|
msgstr "Klik: ten rozjazd krzyżowy zostanie zastąpiony wybranym"
|
||||||
|
|
||||||
|
msgid "Click: it goes here"
|
||||||
|
msgstr "Klik: stanie tutaj"
|
||||||
|
|||||||
Reference in New Issue
Block a user