diff --git a/application/editormode.h b/application/editormode.h index 717725a5..5df3f4c2 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1072,9 +1072,6 @@ class editor_mode : public application_mode, private editor_track::observer editor_track::snap_target origin; editor_track::snap_target snap; glm::vec2 pressed{0.f}; - bool slides{false}; - double slide_low{0.0}; - double slide_high{0.0}; std::vector line; }; extend_tool m_extend; @@ -1143,7 +1140,7 @@ class editor_mode : public application_mode, private editor_track::observer bool plan_branch_link(editor_track::branch_link &Link, std::string &Reason) const; void finish_branch_link(editor_track::branch_link const &Link); std::string link_problem(editor_track::snap_target const &Target, editor_track::branch_link const &Link) const; - std::vector link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, std::vector> &States, std::vector &Created); + std::vector link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, TTrack const &Style, std::vector> &States, std::vector &Created); bool cut_straight(editor_track::straight const &Line, double const From, double const To, std::vector> &States, std::vector &Created, std::vector &Removed, TTrack **Style); 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); diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index ad5a84ce..999d74b8 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -1470,7 +1470,7 @@ editor_mode::selftest_result editor_mode::selftest_switch(std::string const &com words >> point.x >> point.z >> target.x >> target.z; std::replace(label.begin(), label.end(), '_', ' '); auto const log_link = [&](editor_track::branch_link const &Link, std::string const &Reason) { - WriteLog(format("SELFTEST %s plan: valid %d, shift %.3f m, R %.1f m, before %.3f m, after %.3f m, trim %.3f m, shortfall %.3f m, %zu pieces%s", command.c_str(), Link.valid ? 1 : 0, Link.shift, Link.radius, Link.before, Link.after, Link.trim, + WriteLog(format("SELFTEST %s plan: valid %d, R %.1f m, before %.3f m, after %.3f m, trim %.3f m, shortfall %.3f m, %zu pieces%s", command.c_str(), Link.valid ? 1 : 0, Link.radius, Link.before, Link.after, Link.trim, Link.shortfall, Link.pieces.size(), Reason.empty() ? "" : (", " + Reason).c_str())); for (auto const &piece : Link.pieces) { diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 7a208895..f84d4643 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -116,12 +116,43 @@ editor_track::branch_end branch_target(editor_track::snap_target const &Target) return {Target.position, plan_of(Target.direction), grade_of(Target.direction), Target.track != nullptr ? editor_track::joint_roll(*Target.track, Target.point) : 0.0}; } -std::string link_summary(editor_track::snap_target const &Target, editor_track::branch_link const &Link) +double switch_from(editor_track::straight const &Line, double const Length, double const Along, int const Direction, bool const Snap) +{ + auto from{std::clamp(Direction > 0 ? Along : Along - Length, 0.0, Line.length - Length)}; + if (false == Snap) + return from; + std::vector joints{0.0, Line.length}; + for (std::size_t i = 0; i < Line.tracks.size(); ++i) + { + auto const &path{Line.tracks[i]->m_paths[Line.paths[i]]}; + for (auto const index : {segment_data::point::start, segment_data::point::end}) + joints.push_back(Line.along(path.points[index])); + } + auto const nearest = [&](double const Value) { + double best{Value}; + for (auto const joint : joints) + if (std::abs(joint - Value) < std::abs(best - Value) || best == Value) + best = std::abs(joint - Value) < 1.0 ? joint : best; + return best; + }; + auto const snappedstart{nearest(from)}; + auto const snappedend{nearest(from + Length)}; + if (snappedstart != from) + from = snappedstart; + else if (snappedend != from + Length) + from = snappedend - Length; + return std::clamp(from, 0.0, Line.length - Length); +} + +std::string link_summary(editor_track::snap_target const &Target, editor_track::branch_link const &Link, double const Switchradius) { auto const name{Target.track != nullptr && false == Target.track->name().empty() ? Target.track->name() : std::string{"(noname)"}}; - auto text{Link.radius > 0.0 ? format(STR_C("The branch leads into %s with an R %.0f m curve"), name.c_str(), Link.radius) : format(STR_C("The branch leads straight into %s"), name.c_str())}; - if (std::abs(Link.shift) >= 0.005) - text += format(STR_C(", the switch moved %.2f m"), Link.shift); + auto text{Link.reverse ? format(STR_C("The branch leads into %s with reverse curves R %.0f m"), name.c_str(), Link.radius) : + Link.radius > 0.0 ? format(STR_C("The branch leads into %s with an R %.0f m curve"), name.c_str(), Link.radius) : format(STR_C("The branch leads straight into %s"), name.c_str())}; + if (Link.insert > 0.0) + text += format(STR_C(", a straight of %.1f m between them"), Link.insert); + if (Link.reverse && Link.radius < Switchradius - 0.5) + text += format(STR_C(", sharper than the switch (R %.0f m)"), Switchradius); if (Link.trim > 0.0) text += format(STR_C(", %s shortened %.2f m"), name.c_str(), Link.trim); return text; @@ -3749,7 +3780,7 @@ std::string editor_mode::track_readout() const editor_track::branch_link link; std::string reason; if (plan_branch_link(link, reason)) - return link_summary(target, link); + return link_summary(target, link, editor_track::branch_radius(m_extend.track->m_paths[1])); if (snapped && pieces.empty()) return "no track fits to the end of " + name(target.track); auto text{radius > 0.0 ? format("L %.2f m R %.0f m", length, radius) : format("L %.2f m straight", length)}; @@ -4092,19 +4123,7 @@ bool editor_mode::start_extend(editor_track::snap_target const &End) m_extend.pressed = {io.MousePos.x, io.MousePos.y}; m_extend.active = true; if (track->eType == tt_Switch && End.point.path == 1 && End.point.kind == editor_track::point_kind::end) - { - double low{0.0}; - double high{0.0}; - std::string reason; - auto const axis{editor_track::main_axis(*track)}; - if (axis.has_value() && switch_slides(*track) && switch_room(*track, low, high, reason)) - { - m_extend.slides = true; - m_extend.slide_low = low; - m_extend.slide_high = high - axis->length; - } m_extend.line = editor_track::find_straight(*track, m_straights.tolerance).tracks; - } return true; } @@ -5260,31 +5279,7 @@ bool editor_mode::place_switch_on_straight(editor_track::straight const &Line, e m_switch.status = format(STR_C("The straight is %.1f m long, too short for the %.1f m switch"), Line.length, shape.length); return false; } - auto from{std::clamp(Direction > 0 ? Along : Along - shape.length, 0.0, Line.length - shape.length)}; - if (Snap) - { - std::vector joints{0.0, Line.length}; - for (std::size_t i = 0; i < Line.tracks.size(); ++i) - { - auto const &path{Line.tracks[i]->m_paths[Line.paths[i]]}; - for (auto const index : {segment_data::point::start, segment_data::point::end}) - joints.push_back(Line.along(path.points[index])); - } - auto const nearest = [&](double const Value) { - double best{Value}; - for (auto const joint : joints) - if (std::abs(joint - Value) < std::abs(best - Value) || best == Value) - best = std::abs(joint - Value) < 1.0 ? joint : best; - return best; - }; - auto const snappedstart{nearest(from)}; - auto const snappedend{nearest(from + shape.length)}; - if (snappedstart != from) - from = snappedstart; - else if (snappedend != from + shape.length) - from = snappedend - shape.length; - from = std::clamp(from, 0.0, Line.length - shape.length); - } + auto const from{switch_from(Line, shape.length, Along, Direction, Snap)}; auto const to{from + shape.length}; auto const origin{Direction > 0 ? from : to}; TTrack *first{nullptr}; @@ -5346,11 +5341,6 @@ void editor_mode::draw_build_overlay() const ImVec2 screen; if (projection.project(target.position, screen)) drawlist->AddCircle(screen, 14.0f, IM_COL32(255, 60, 255, 255), 20, 3.0f); - editor_track::branch_link link; - std::string reason; - if (plan_branch_link(link, reason) && std::abs(link.shift) >= 0.005) - for (auto const &path : editor_track::slid(*m_extend.track, link.shift)) - drawpath(path, overlay_color::grip); for (auto const &piece : extend_pieces()) drawpath(piece, overlay_color::marked); } @@ -5846,7 +5836,7 @@ std::string editor_mode::link_problem(editor_track::snap_target const &Target, e { auto const name{Target.track != nullptr && false == Target.track->name().empty() ? Target.track->name() : std::string{"(noname)"}}; if (false == Link.valid) - return Link.shortfall > 0.0 ? format(STR_C("The branch can't lead into %s: the tracks are %.2f m too close for this switch with its curve"), name.c_str(), Link.shortfall) : + return Link.shortfall > 0.0 ? format(STR_C("The branch can't lead into %s: the R %.0f m curve needs %.2f m more room"), name.c_str(), Link.radius, Link.shortfall) : format(STR_C("The branch can't lead into %s"), name.c_str()); if (Link.trim > 0.0) { @@ -5892,23 +5882,17 @@ bool editor_mode::plan_switch_link(switch_link &Plan) const if (Plan.target.track == nullptr) return false; } - auto const paths{place(tool.along)}; - if (paths.size() < 2) + if (shape.length > line.length - 0.02) return false; - glm::dvec2 const direction{line.direction * static_cast(Plan.forward)}; - Plan.link = editor_track::link_branch(branch_start(paths[1]), {direction.x, line.grade * Plan.forward, direction.y}, editor_track::branch_radius(paths[1]), branch_target(Plan.target), glm::radians(m_joints.angle * 0.5)); - Plan.reason = link_problem(Plan.target, Plan.link); - if (false == Plan.reason.empty()) - return false; - Plan.along = tool.along + Plan.link.shift * Plan.forward; - auto const from{Plan.forward > 0 ? Plan.along : Plan.along - shape.length}; - if (from < -1e-6 || from + shape.length > line.length + 1e-6) - { - Plan.reason = format(STR_C("The switch would have to move %.1f m, off the straight it stands on"), Plan.link.shift); - return false; - } + auto const from{switch_from(line, shape.length, tool.along, Plan.forward, true)}; + Plan.along = Plan.forward > 0 ? from : from + shape.length; Plan.paths = place(Plan.along); - return true; + if (Plan.paths.size() < 2) + return false; + Plan.link = editor_track::link_branch(branch_start(Plan.paths[1]), editor_track::branch_radius(Plan.paths[1]), branch_target(Plan.target), glm::radians(m_joints.angle * 0.5), + editor_track::reverse_insert(design_speed(line.tracks.front(), Plan.target.track))); + Plan.reason = link_problem(Plan.target, Plan.link); + return Plan.reason.empty(); } void editor_mode::insert_linked_switch(switch_link const &Plan) @@ -5919,12 +5903,12 @@ void editor_mode::insert_linked_switch(switch_link const &Plan) if (false == place_switch_on_straight(m_switch.line, m_switch.templates[m_switch.armed], nullptr, Plan.along, Plan.forward, Plan.side, false, states, created, removed, switch_name_for_new())) return; auto const placed{created}; - link_tracks(Plan.target, Plan.link, states, created); + link_tracks(Plan.target, Plan.link, Plan.target.track->eType == tt_Normal ? *Plan.target.track : *m_switch.line.tracks.front(), states, created); push_track_snapshot(std::move(states), std::move(created), std::move(removed)); straight_refresh(); m_switch.status.clear(); switch_placed(placed); - m_switch.status = link_summary(Plan.target, Plan.link) + (m_switch.status.empty() ? std::string{} : "\n" + m_switch.status); + m_switch.status = link_summary(Plan.target, Plan.link, editor_track::branch_radius(Plan.paths[1])) + (m_switch.status.empty() ? std::string{} : "\n" + m_switch.status); ui()->set_status(m_switch.status, false); } @@ -5937,22 +5921,10 @@ bool editor_mode::plan_branch_link(editor_track::branch_link &Link, std::string return false; if (std::find(tool.line.begin(), tool.line.end(), target.track) != tool.line.end()) return false; - auto const axis{editor_track::main_axis(*tool.track)}; - if (false == axis.has_value()) - return false; - auto const &main{tool.track->m_paths.front()}; auto const &diverging{tool.track->m_paths[1]}; - auto const rise{grade_of(main.points[segment_data::point::end] - main.points[segment_data::point::start])}; - Link = editor_track::link_branch(branch_start(diverging), {axis->direction.x, rise, axis->direction.y}, editor_track::branch_radius(diverging), branch_target(target), glm::radians(m_joints.angle * 0.5)); + Link = editor_track::link_branch(branch_start(diverging), editor_track::branch_radius(diverging), branch_target(target), glm::radians(m_joints.angle * 0.5), editor_track::reverse_insert(design_speed(tool.track, target.track))); Reason = link_problem(target, Link); - if (false == Reason.empty()) - return false; - if (std::abs(Link.shift) >= 0.005 && (false == tool.slides || Link.shift < tool.slide_low - 1e-6 || Link.shift > tool.slide_high + 1e-6)) - { - Reason = format(STR_C("The switch would have to move %.1f m, there's no plain track for it there"), Link.shift); - return false; - } - return true; + return Reason.empty(); } void editor_mode::finish_branch_link(editor_track::branch_link const &Link) @@ -5960,26 +5932,18 @@ void editor_mode::finish_branch_link(editor_track::branch_link const &Link) auto *own{m_extend.track}; auto const target{m_extend.snap}; m_extend.active = false; - auto link{Link}; - bool const slides{std::abs(link.shift) >= 0.005}; - if (slides && false == slide_switch(*own, link.shift)) - return; - if (false == slides && false == link.pieces.empty()) - link.pieces.front().points[segment_data::point::start] = own->m_paths[1].points[segment_data::point::end]; std::vector> states; std::vector created; - auto const linked{link_tracks(target, link, states, created)}; - bool const changed{false == states.empty() || false == created.empty()}; + auto const &style{target.track->eType == tt_Normal || m_extend.line.empty() ? *target.track : *m_extend.line.front()}; + auto const linked{link_tracks(target, Link, style, states, created)}; push_track_snapshot(std::move(states), std::move(created)); - if (slides && changed) - m_history.back().joined = true; m_node = linked.empty() ? own : linked.back(); ui()->set_node(m_node); straight_refresh(); - ui()->set_status(link_summary(target, link), false); + ui()->set_status(link_summary(target, Link, editor_track::branch_radius(own->m_paths[1])), false); } -std::vector editor_mode::link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, std::vector> &States, std::vector &Created) +std::vector editor_mode::link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, TTrack const &Style, std::vector> &States, std::vector &Created) { auto *target{Target.track}; if (Link.trim > 0.0) @@ -5991,7 +5955,7 @@ std::vector editor_mode::link_tracks(editor_track::snap_target const & } std::vector pieces; for (auto const &piece : Link.pieces) - pieces.push_back(editor_track::create_path(*target, piece)); + pieces.push_back(editor_track::create_path(Style, piece)); editor_track::commit(pieces); digitize_drawn(pieces); Created.insert(Created.end(), pieces.begin(), pieces.end()); diff --git a/editor/editorTrack.hpp b/editor/editorTrack.hpp index b8a4e710..ad1ab085 100644 --- a/editor/editorTrack.hpp +++ b/editor/editorTrack.hpp @@ -214,16 +214,18 @@ class editor_track struct branch_link { bool valid{false}; - double shift{0.0}; double trim{0.0}; double radius{0.0}; double before{0.0}; double after{0.0}; double shortfall{0.0}; + bool reverse{false}; + double insert{0.0}; std::vector pieces; }; static double branch_radius(segment_data const &Diverging); - static branch_link link_branch(branch_end const &From, glm::dvec3 const &Slide, double const Radius, branch_end const &To, double const Kink); + static double reverse_insert(double const Speed); + static branch_link link_branch(branch_end const &From, double const Radius, branch_end const &To, double const Kink, double const Insert); struct snap_target { diff --git a/editor/editorTrackShape.cpp b/editor/editorTrackShape.cpp index 8b50984f..9cc4cc3d 100644 --- a/editor/editorTrackShape.cpp +++ b/editor/editorTrackShape.cpp @@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/. #include #include #include +#include namespace { @@ -378,78 +379,170 @@ double editor_track::branch_radius(segment_data const &Diverging) return turn > 1e-4 ? curve.plan_length() / turn : 0.0; } -editor_track::branch_link editor_track::link_branch(branch_end const &From, glm::dvec3 const &Slide, double const Radius, branch_end const &To, double const Kink) +double editor_track::reverse_insert(double const Speed) +{ + if (Speed <= 60.0) + return 6.0; + return Speed <= 100.0 ? 0.15 * Speed : 0.25 * Speed; +} + +namespace +{ + +double constexpr kPi{3.14159265358979323846}; +double constexpr kHalfPi{0.5 * kPi}; +double constexpr kReverseNoInsert{106.5}; + +glm::dvec2 unit_chord(glm::dvec2 const &Heading, double const Angle) +{ + glm::dvec2 const left{-Heading.y, Heading.x}; + return Heading * std::sin(std::abs(Angle)) + left * ((Angle > 0.0 ? 1.0 : -1.0) * (1.0 - std::cos(Angle))); +} + +struct reverse_fit +{ + double radius{0.0}; + double first{0.0}; + double second{0.0}; +}; + +std::optional fit_reverse(glm::dvec2 const &Offset, glm::dvec2 const &Tangent, double const Turn, double const Insert) +{ + auto const residual = [&](double const First, double &Radius) { + auto const heading{turned(Tangent, First)}; + auto const chord{unit_chord(Tangent, First) + unit_chord(heading, Turn - First)}; + auto const rest{Offset - heading * Insert}; + Radius = glm::dot(chord, rest) / std::max(glm::dot(chord, chord), 1e-12); + return cross(chord, rest); + }; + auto const usable = [&](double const First) { return std::abs(First) > 1e-4 && std::abs(Turn - First) > 1e-4 && (First > 0.0) != (Turn - First > 0.0) && std::abs(Turn - First) <= kHalfPi; }; + std::optional best; + int constexpr steps{2000}; + double radius{0.0}; + auto previous{-kHalfPi}; + auto before{residual(previous, radius)}; + for (int i = 1; i <= steps; ++i) + { + auto const first{-kHalfPi + kPi * i / steps}; + auto const now{residual(first, radius)}; + if (usable(previous) && usable(first) && (before > 0.0) != (now > 0.0)) + { + auto low{previous}; + auto high{first}; + auto lowvalue{before}; + for (int k = 0; k < 60; ++k) + { + auto const middle{0.5 * (low + high)}; + auto const value{residual(middle, radius)}; + if ((value > 0.0) == (lowvalue > 0.0)) + { + low = middle; + lowvalue = value; + } + else + high = middle; + } + auto const root{0.5 * (low + high)}; + residual(root, radius); + if (radius >= 1.0 && (false == best.has_value() || radius > best->radius)) + best = reverse_fit{radius, root, Turn - root}; + } + previous = first; + before = now; + } + return best; +} + +} // namespace + +editor_track::branch_link editor_track::link_branch(branch_end const &From, double const Radius, branch_end const &To, double const Kink, double const Insert) { branch_link result; - if (glm::length(From.direction) < 1e-9 || glm::length(To.direction) < 1e-9 || glm::length(plan_of(Slide)) < 1e-9) + if (glm::length(From.direction) < 1e-9 || glm::length(To.direction) < 1e-9 || Radius < 1.0) return result; - auto const slide{glm::normalize(plan_of(Slide))}; - auto const rise{Slide.y / glm::length(plan_of(Slide))}; auto const tangent{glm::normalize(From.direction)}; auto const into{glm::normalize(To.direction)}; auto const turn{signed_angle(tangent, into)}; auto const start{plan_of(From.position)}; auto const target{plan_of(To.position)}; - auto const across{cross(into, slide)}; + auto const offset{target - start}; if (std::abs(turn) > glm::radians(170.0)) return result; - std::vector arc; - glm::dvec2 chord{0.0}; - if (std::abs(turn) > Kink) - { - if (Radius < 1.0) - return result; - arc = arc_pieces(glm::dvec3{0.0}, tangent, 0.0, Radius, std::abs(turn), turn > 0.0 ? 1 : -1); - chord = plan_of(arc.back().points[segment_data::point::end]); - result.radius = Radius; - } - else if (std::abs(across) < 1e-6) - return result; - if (false == arc.empty() && std::abs(across) < std::sin(glm::radians(1.0))) - { - auto const gap{target - start - chord}; - auto const determinant{cross(tangent, slide)}; - if (std::abs(determinant) < 1e-6) - return result; - result.before = cross(gap, slide) / determinant; - result.shift = cross(tangent, gap) / determinant; - if (result.before < -0.01) + std::vector> arcs; + auto const reverse = [&]() { + auto fit{fit_reverse(offset, tangent, turn, 0.0)}; + if (false == fit.has_value() || 2.0 / fit->radius > 1.0 / kReverseNoInsert) { - result.shortfall = -result.before * std::abs(std::sin(turn)); - return result; + fit = fit_reverse(offset, tangent, turn, Insert); + result.insert = Insert; } - result.before = std::max(0.0, result.before); + if (false == fit.has_value()) + return false; + result.reverse = true; + result.radius = fit->radius; + result.before = 0.0; + result.after = 0.0; + result.shortfall = 0.0; + arcs = {{std::abs(fit->first), fit->first > 0.0 ? 1 : -1}, {std::abs(fit->second), fit->second > 0.0 ? 1 : -1}}; + return true; + }; + if (std::abs(turn) <= Kink) + { + auto const lateral{cross(tangent, offset)}; + result.after = glm::dot(tangent, offset); + if (std::abs(lateral) >= 0.005 && false == reverse()) + return result; } else { - result.shift = cross(into, target - start - chord) / across; - result.after = glm::dot(target - (start + slide * result.shift + chord), into); - if (result.after < -0.01) + auto const determinant{cross(tangent, into)}; + auto const reach{cross(offset, into) / determinant}; + auto const tangentlength{Radius * std::tan(std::abs(turn) * 0.5)}; + result.before = reach - tangentlength; + result.after = cross(tangent, offset) / determinant - tangentlength; + if (reach > 0.0 && result.before >= -0.01) { - result.trim = -result.after; - result.after = 0.0; + result.before = std::max(0.0, result.before); + arcs = {{std::abs(turn), turn > 0.0 ? 1 : -1}}; + result.radius = Radius; + } + else if (false == reverse()) + { + result.shortfall = std::max(0.01, -result.before); + return result; } } - auto const origin{glm::dvec3{start.x, From.position.y, start.y} + glm::dvec3{slide.x, rise, slide.y} * result.shift}; + if (result.after < -0.01) + { + result.trim = -result.after; + result.after = 0.0; + } auto const line = [](glm::dvec2 const &A, glm::dvec2 const &B) { segment_data path; path.points[segment_data::point::start] = {A.x, 0.0, A.y}; path.points[segment_data::point::end] = {B.x, 0.0, B.y}; return path; }; - auto cursor{plan_of(origin)}; + auto cursor{start}; + auto heading{tangent}; if (result.before >= 0.01) { result.pieces.push_back(line(cursor, cursor + tangent * result.before)); cursor += tangent * result.before; } - for (auto piece : arc) + for (std::size_t i = 0; i < arcs.size(); ++i) { - for (auto const index : {segment_data::point::start, segment_data::point::end}) - piece.points[index] += glm::dvec3{cursor.x, 0.0, cursor.y}; - result.pieces.push_back(piece); + auto const [angle, side]{arcs[i]}; + if (i > 0 && result.insert >= 0.01) + { + result.pieces.push_back(line(cursor, cursor + heading * result.insert)); + cursor += heading * result.insert; + } + auto const pieces{arc_pieces({cursor.x, 0.0, cursor.y}, heading, 0.0, result.radius, angle, side)}; + result.pieces.insert(result.pieces.end(), pieces.begin(), pieces.end()); + cursor = plan_of(pieces.back().points[segment_data::point::end]); + heading = turned(heading, side * angle); } - cursor += chord; if (result.after >= 0.01) result.pieces.push_back(line(cursor, target)); else if (result.trim == 0.0 && false == result.pieces.empty()) @@ -457,7 +550,7 @@ editor_track::branch_link editor_track::link_branch(branch_end const &From, glm: if (false == result.pieces.empty()) { auto const height1{result.trim > 0.0 ? To.position.y + To.grade * result.trim : To.position.y}; - ground_follow::follow(result.pieces, {origin.y, From.grade, height1, To.grade}, [](std::vector const &Points, std::vector const &) { return std::vector(Points.size(), std::numeric_limits::quiet_NaN()); }); + ground_follow::follow(result.pieces, {From.position.y, From.grade, height1, To.grade}, [](std::vector const &Points, std::vector const &) { return std::vector(Points.size(), std::numeric_limits::quiet_NaN()); }); std::vector lengths; double total{0.0}; for (auto const &piece : result.pieces) diff --git a/lang/editor_pl.po b/lang/editor_pl.po index f90351e8..dd3af8ca 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -5455,21 +5455,17 @@ msgstr "Zwrotnica %s: napęd %s, teraz po lewej" msgid "Switch %s without a drive now" msgstr "Zwrotnica %s jest teraz bez napędu" - msgid "The branch leads into %s with an R %.0f m curve" msgstr "Odgałęzienie wchodzi w %s łukiem R %.0f m" msgid "The branch leads straight into %s" msgstr "Odgałęzienie wchodzi w %s prostą" -msgid ", the switch moved %.2f m" -msgstr ", rozjazd przesunięty o %.2f m" - msgid ", %s shortened %.2f m" msgstr ", %s skrócony o %.2f m" -msgid "The branch can't lead into %s: the tracks are %.2f m too close for this switch with its curve" -msgstr "Odgałęzienie nie wejdzie w %s: tory są o %.2f m za blisko dla tego rozjazdu z łukiem" +msgid "The branch can't lead into %s: the R %.0f m curve needs %.2f m more room" +msgstr "Odgałęzienie nie wejdzie w %s: łuk R %.0f m potrzebuje %.2f m więcej miejsca" msgid "The branch can't lead into %s" msgstr "Odgałęzienie nie wejdzie w %s" @@ -5477,8 +5473,11 @@ msgstr "Odgałęzienie nie wejdzie w %s" msgid "%s would have to be shortened by %.2f m, it's too short or not straight" msgstr "%s trzeba by skrócić o %.2f m, a jest za krótki albo nie jest prosty" -msgid "The switch would have to move %.1f m, off the straight it stands on" -msgstr "Rozjazd musiałby się przesunąć o %.1f m, poza prostą, na której stoi" +msgid "The branch leads into %s with reverse curves R %.0f m" +msgstr "Odgałęzienie wchodzi w %s łukami odwrotnymi R %.0f m" -msgid "The switch would have to move %.1f m, there's no plain track for it there" -msgstr "Rozjazd musiałby się przesunąć o %.1f m, a tam nie ma zwykłego toru" +msgid ", a straight of %.1f m between them" +msgstr ", między nimi prosta %.1f m" + +msgid ", sharper than the switch (R %.0f m)" +msgstr ", ostrzej niż rozjazd (R %.0f m)"