mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 06:19:19 +02:00
feat(editor): the branch of a switch leads into the free end of a track: when the angles match the switch slides so its branch lies on the track's line, otherwise an arc of the switch radius right after the switch turns into it (with a straight between for a parallel track); works while placing a switch (drag toward the end) and when dragging from the branch end of a placed one, the track is shortened when it reaches past the branch; self-test switchlink / branchlink
This commit is contained in:
@@ -1072,6 +1072,10 @@ 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<TTrack *> line;
|
||||
};
|
||||
extend_tool m_extend;
|
||||
// lays new track through the clicked points, with no path needed to start from
|
||||
@@ -1124,6 +1128,22 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
std::vector<segment_data> paths;
|
||||
};
|
||||
bool plan_crossover(crossover_plan &Plan) const;
|
||||
struct switch_link
|
||||
{
|
||||
editor_track::snap_target target;
|
||||
editor_track::branch_link link;
|
||||
double along{0.0};
|
||||
int forward{1};
|
||||
int side{1};
|
||||
std::vector<segment_data> paths;
|
||||
std::string reason;
|
||||
};
|
||||
bool plan_switch_link(switch_link &Plan) const;
|
||||
void insert_linked_switch(switch_link const &Plan);
|
||||
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<TTrack *> link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, 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 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);
|
||||
|
||||
@@ -1460,6 +1460,104 @@ editor_mode::selftest_result editor_mode::selftest_switch(std::string const &com
|
||||
WriteLog("SELFTEST " + command + ": " + tool.status + " " + m_straights.status);
|
||||
return selftest_result::handled;
|
||||
}
|
||||
if (command == "switchlink" || command == "branchlink")
|
||||
{
|
||||
std::string label;
|
||||
glm::dvec3 point{0.0};
|
||||
glm::dvec3 target{0.0};
|
||||
if (command == "switchlink")
|
||||
words >> label;
|
||||
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,
|
||||
Link.shortfall, Link.pieces.size(), Reason.empty() ? "" : (", " + Reason).c_str()));
|
||||
for (auto const &piece : Link.pieces)
|
||||
{
|
||||
auto const &a{piece.points[segment_data::point::start]};
|
||||
auto const &b{piece.points[segment_data::point::end]};
|
||||
WriteLog(format("SELFTEST %s piece: %.3f %.3f %.3f -> %.3f %.3f %.3f, L %.3f m, R %.1f m", command.c_str(), a.x, a.y, a.z, b.x, b.y, b.z, geometry::bezier{piece}.plan_length(), static_cast<double>(piece.radius)));
|
||||
}
|
||||
};
|
||||
if (command == "switchlink")
|
||||
{
|
||||
show_track_tab(track_tab::turnout);
|
||||
auto &tool{m_switch};
|
||||
tool.templates = editor_track::standard_switch_templates();
|
||||
tool.collected = true;
|
||||
tool.armed = -1;
|
||||
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
|
||||
if (tool.templates[i].label.rfind(label, 0) == 0)
|
||||
{
|
||||
tool.armed = i;
|
||||
break;
|
||||
}
|
||||
TTrack *nearest{nullptr};
|
||||
double distance{1e9};
|
||||
for (auto *track : simulation::Paths.sequence())
|
||||
{
|
||||
if (track == nullptr || track->m_editorremoved || track->eType != tt_Normal || track->m_paths.empty())
|
||||
continue;
|
||||
auto const d{geometry::plan_distance(editor_track::point_at(*track, editor_track::nearest_parameter(*track, point)), point)};
|
||||
if (d < distance)
|
||||
{
|
||||
distance = d;
|
||||
nearest = track;
|
||||
}
|
||||
}
|
||||
if (tool.armed < 0 || nearest == nullptr)
|
||||
{
|
||||
WriteLog("SELFTEST switchlink: no template " + label + " or no path");
|
||||
return selftest_result::handled;
|
||||
}
|
||||
m_node = nearest;
|
||||
ui()->set_node(m_node);
|
||||
m_cursor_override = point;
|
||||
if (false == start_switch_placement())
|
||||
{
|
||||
m_cursor_override.reset();
|
||||
WriteLog("SELFTEST switchlink: not started, " + tool.status);
|
||||
return selftest_result::handled;
|
||||
}
|
||||
tool.mouse = glm::dvec3{target.x, tool.point.y, target.z};
|
||||
switch_link plan;
|
||||
plan_switch_link(plan);
|
||||
log_link(plan.link, plan.reason);
|
||||
finish_switch_placement();
|
||||
m_cursor_override.reset();
|
||||
WriteLog("SELFTEST switchlink: " + tool.status);
|
||||
return selftest_result::handled;
|
||||
}
|
||||
TTrack *own{nullptr};
|
||||
for (auto *track : simulation::Paths.sequence())
|
||||
if (track != nullptr && false == track->m_editorremoved && track->eType == tt_Switch && track->m_paths.size() == 2 && geometry::plan_distance(track->m_paths[1].points[segment_data::point::end], point) < 3.0)
|
||||
own = track;
|
||||
if (own == nullptr)
|
||||
{
|
||||
WriteLog("SELFTEST branchlink: no switch branch end there");
|
||||
return selftest_result::handled;
|
||||
}
|
||||
editor_track::snap_target end;
|
||||
end.track = own;
|
||||
end.point = {1, editor_track::point_kind::end};
|
||||
end.position = own->m_paths[1].points[segment_data::point::end];
|
||||
if (false == start_extend(end))
|
||||
{
|
||||
WriteLog("SELFTEST branchlink: the branch end isn't free");
|
||||
return selftest_result::handled;
|
||||
}
|
||||
m_extend.pressed = {-10000.f, -10000.f};
|
||||
m_extend.mouse = glm::dvec3{target.x, m_extend.point.y, target.z};
|
||||
m_extend.snap = editor_track::find_free_end(own, own->iCategoryFlag & 15, m_extend.mouse, 3.0, {own});
|
||||
editor_track::branch_link link;
|
||||
std::string reason;
|
||||
plan_branch_link(link, reason);
|
||||
log_link(link, reason);
|
||||
finish_extend();
|
||||
WriteLog(format("SELFTEST branchlink: switch now %.3f %.3f -> %.3f %.3f", own->m_paths[0].points[segment_data::point::start].x, own->m_paths[0].points[segment_data::point::start].z, own->m_paths[0].points[segment_data::point::end].x,
|
||||
own->m_paths[0].points[segment_data::point::end].z));
|
||||
return selftest_result::handled;
|
||||
}
|
||||
if (command == "switchslide" || command == "switchreplace")
|
||||
{
|
||||
std::string name;
|
||||
|
||||
@@ -105,6 +105,28 @@ double const kJointRange{400.0}; // the ends of the paths are drawn for the trac
|
||||
ImU32 const kJointColor{IM_COL32(255, 255, 255, 200)};
|
||||
int const kRailCategory{1};
|
||||
|
||||
editor_track::branch_end branch_start(segment_data const &Diverging)
|
||||
{
|
||||
auto const tangent{bezier{Diverging}.first(1.0)};
|
||||
return {Diverging.points[segment_data::point::end], plan_of(tangent), grade_of(tangent), static_cast<double>(Diverging.rolls[1])};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (Link.trim > 0.0)
|
||||
text += format(STR_C(", %s shortened %.2f m"), name.c_str(), Link.trim);
|
||||
return text;
|
||||
}
|
||||
|
||||
std::optional<alignment::join_result> eased_join(alignment::join_request const &Request, editor_track::easing const &In, editor_track::easing const &Out)
|
||||
{
|
||||
if (In.pieces.empty() && Out.pieces.empty())
|
||||
@@ -3724,11 +3746,17 @@ std::string editor_mode::track_readout() const
|
||||
}
|
||||
editor_track::snap_target target;
|
||||
auto const snapped{extend_snap(target) && target.track != nullptr};
|
||||
editor_track::branch_link link;
|
||||
std::string reason;
|
||||
if (plan_branch_link(link, reason))
|
||||
return link_summary(target, link);
|
||||
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)};
|
||||
if (snapped)
|
||||
text += "\njoins " + name(target.track);
|
||||
if (false == reason.empty())
|
||||
text += "\n" + reason;
|
||||
return text + "\n" + track_shaping(snapped);
|
||||
}
|
||||
if (m_lay.active)
|
||||
@@ -4063,6 +4091,20 @@ bool editor_mode::start_extend(editor_track::snap_target const &End)
|
||||
m_extend.origin = 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;
|
||||
}
|
||||
|
||||
@@ -4301,6 +4343,13 @@ std::vector<segment_data> editor_mode::extend_pieces() const
|
||||
editor_track::snap_target target;
|
||||
if (extend_snap(target))
|
||||
{
|
||||
editor_track::branch_link link;
|
||||
std::string reason;
|
||||
if (plan_branch_link(link, reason))
|
||||
{
|
||||
m_join_last = {};
|
||||
return link.pieces;
|
||||
}
|
||||
double length;
|
||||
std::string error;
|
||||
return join_pieces(tool.track, {tool.path, tool.atend ? editor_track::point_kind::end : editor_track::point_kind::start}, tool.point, tool.direction, target, length, error);
|
||||
@@ -4313,10 +4362,9 @@ std::vector<segment_data> editor_mode::extend_pieces() const
|
||||
auto const begin{plan_of(curve.first(0.0))};
|
||||
auto const finish{plan_of(curve.first(1.0))};
|
||||
auto const turn{glm::length(begin) > 1e-9 && glm::length(finish) > 1e-9 ? geometry::signed_angle(begin, finish) : 0.0};
|
||||
if (std::abs(turn) > 1e-4)
|
||||
if (auto const radius{editor_track::branch_radius(diverging)}; radius > 0.0)
|
||||
{
|
||||
int const side{turn > 0.0 ? 1 : -1};
|
||||
auto const radius{curve.plan_length() / std::abs(turn)};
|
||||
auto const &direction{tool.direction};
|
||||
auto const centre{plan_of(tool.point) + glm::dvec2{-direction.y, direction.x} * (side * radius)};
|
||||
auto const angle{geometry::signed_angle(plan_of(tool.point) - centre, plan_of(tool.mouse) - centre) * side};
|
||||
@@ -4401,6 +4449,14 @@ void editor_mode::finish_extend()
|
||||
m_track_point = m_extend.origin.point;
|
||||
return;
|
||||
}
|
||||
std::string reason;
|
||||
if (editor_track::branch_link link; plan_branch_link(link, reason))
|
||||
{
|
||||
finish_branch_link(link);
|
||||
return;
|
||||
}
|
||||
if (false == reason.empty())
|
||||
ui()->set_status(reason, true);
|
||||
crossover_plan plan;
|
||||
editor_track::snap_target target;
|
||||
if (false == extend_snap(target) && plan_crossover(plan))
|
||||
@@ -4924,6 +4980,11 @@ std::vector<segment_data> editor_mode::switch_preview() const
|
||||
auto const side{direction.x * offset.y - direction.y * offset.x >= 0.0 ? 1 : -1};
|
||||
return curved_switch_paths(tool.templates[tool.armed], tool.frame, tool.along, forward, side);
|
||||
}
|
||||
if (switch_link plan; plan_switch_link(plan))
|
||||
{
|
||||
plan.paths.insert(plan.paths.end(), plan.link.pieces.begin(), plan.link.pieces.end());
|
||||
return plan.paths;
|
||||
}
|
||||
auto const &line{tool.line};
|
||||
glm::dvec2 const offset{tool.mouse.x - tool.point.x, tool.mouse.z - tool.point.z};
|
||||
auto const forward{glm::dot(offset, line.direction) >= 0.0 ? 1 : -1};
|
||||
@@ -5005,9 +5066,16 @@ bool editor_mode::start_switch_placement()
|
||||
void editor_mode::finish_switch_placement()
|
||||
{
|
||||
auto &tool{m_switch};
|
||||
switch_link plan;
|
||||
auto const linked{plan_switch_link(plan)};
|
||||
tool.placing = false;
|
||||
if (tool.armed < 0 || tool.armed >= static_cast<int>(tool.templates.size()))
|
||||
return;
|
||||
if (linked)
|
||||
{
|
||||
insert_linked_switch(plan);
|
||||
return;
|
||||
}
|
||||
if (tool.curved)
|
||||
{
|
||||
auto const sample{sample_at(tool.frame, tool.along)};
|
||||
@@ -5023,6 +5091,8 @@ void editor_mode::finish_switch_placement()
|
||||
glm::dvec2 const direction{tool.line.direction * static_cast<double>(forward)};
|
||||
auto const side{direction.x * offset.y - direction.y * offset.x >= 0.0 ? 1 : -1};
|
||||
insert_switch(tool.line, tool.along, forward, side);
|
||||
if (false == plan.reason.empty())
|
||||
ui()->set_status(plan.reason, true);
|
||||
}
|
||||
|
||||
void editor_mode::insert_switch(editor_track::straight const &Line, double const Along, int const Direction, int const Side)
|
||||
@@ -5276,6 +5346,11 @@ 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);
|
||||
}
|
||||
@@ -5292,8 +5367,19 @@ void editor_mode::draw_build_overlay() const
|
||||
}
|
||||
}
|
||||
if (m_switch.placing)
|
||||
{
|
||||
for (auto const &piece : switch_preview())
|
||||
drawpath(piece, overlay_color::marked);
|
||||
switch_link plan;
|
||||
auto const linked{plan_switch_link(plan)};
|
||||
ImVec2 screen;
|
||||
if (plan.target.track != nullptr && projection.project(plan.target.position, screen))
|
||||
{
|
||||
drawlist->AddCircle(screen, 14.0f, linked ? IM_COL32(255, 60, 255, 255) : overlay_color::invalid, 20, 3.0f);
|
||||
if (false == linked && false == plan.reason.empty())
|
||||
drawlist->AddText({screen.x + 18.0f, screen.y - 8.0f}, overlay_color::invalid, plan.reason.c_str());
|
||||
}
|
||||
}
|
||||
if (m_lay.active)
|
||||
{
|
||||
auto const &lay{m_lay};
|
||||
@@ -5756,6 +5842,162 @@ bool editor_mode::plan_crossover(crossover_plan &Plan) const
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string editor_mode::link_problem(editor_track::snap_target const &Target, editor_track::branch_link const &Link) const
|
||||
{
|
||||
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) :
|
||||
format(STR_C("The branch can't lead into %s"), name.c_str());
|
||||
if (Link.trim > 0.0)
|
||||
{
|
||||
auto const &path{Target.track->m_paths[Target.point.path]};
|
||||
if (has_controls(path) || geometry::chord_length(path) < Link.trim + 1.0)
|
||||
return format(STR_C("%s would have to be shortened by %.2f m, it's too short or not straight"), name.c_str(), Link.trim);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool editor_mode::plan_switch_link(switch_link &Plan) const
|
||||
{
|
||||
auto const &tool{m_switch};
|
||||
if (false == tool.placing || tool.curved || tool.armed < 0 || tool.armed >= static_cast<int>(tool.templates.size()) || tool.line.tracks.empty() || tool.templates[tool.armed].double_slip)
|
||||
return false;
|
||||
auto const &shape{tool.templates[tool.armed]};
|
||||
auto const &line{tool.line};
|
||||
std::vector<TTrack const *> const exclude(line.tracks.begin(), line.tracks.end());
|
||||
auto const category{line.tracks.front()->iCategoryFlag & 15};
|
||||
auto const orient = [&](glm::dvec3 const &Toward) {
|
||||
glm::dvec2 const offset{plan_of(Toward - tool.point)};
|
||||
Plan.forward = glm::dot(offset, line.direction) >= 0.0 ? 1 : -1;
|
||||
Plan.side = cross(line.direction * static_cast<double>(Plan.forward), offset) >= 0.0 ? 1 : -1;
|
||||
};
|
||||
auto const place = [&](double const Along) {
|
||||
auto const point{line.start + glm::dvec3{line.direction.x, line.grade, line.direction.y} * Along};
|
||||
return editor_track::place_switch(shape, point, line.direction * static_cast<double>(Plan.forward), Plan.side, line.grade * Plan.forward);
|
||||
};
|
||||
auto const &io{ImGui::GetIO()};
|
||||
if (false == m_cursor_override.has_value())
|
||||
Plan.target = snap_free_end(tool.mouse, {io.MousePos.x, io.MousePos.y}, nullptr, category, exclude);
|
||||
if (Plan.target.track == nullptr)
|
||||
Plan.target = editor_track::find_free_end(nullptr, category, tool.mouse, kLaySnapRadius, exclude);
|
||||
if (Plan.target.track != nullptr)
|
||||
orient(Plan.target.position);
|
||||
else
|
||||
{
|
||||
orient(tool.mouse);
|
||||
auto const paths{place(tool.along)};
|
||||
if (paths.size() < 2)
|
||||
return false;
|
||||
Plan.target = editor_track::find_free_end(nullptr, category, paths[1].points[segment_data::point::end], kLaySnapRadius, exclude);
|
||||
if (Plan.target.track == nullptr)
|
||||
return false;
|
||||
}
|
||||
auto const paths{place(tool.along)};
|
||||
if (paths.size() < 2)
|
||||
return false;
|
||||
glm::dvec2 const direction{line.direction * static_cast<double>(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;
|
||||
}
|
||||
Plan.paths = place(Plan.along);
|
||||
return true;
|
||||
}
|
||||
|
||||
void editor_mode::insert_linked_switch(switch_link const &Plan)
|
||||
{
|
||||
std::vector<std::pair<TTrack *, editor_track::state>> states;
|
||||
std::vector<TTrack *> created;
|
||||
std::vector<TTrack *> removed;
|
||||
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);
|
||||
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);
|
||||
ui()->set_status(m_switch.status, false);
|
||||
}
|
||||
|
||||
bool editor_mode::plan_branch_link(editor_track::branch_link &Link, std::string &Reason) const
|
||||
{
|
||||
auto const &tool{m_extend};
|
||||
editor_track::snap_target target;
|
||||
Reason.clear();
|
||||
if (false == extend_snap(target) || tool.track == nullptr || tool.track->eType != tt_Switch || tool.track->m_paths.size() != 2 || tool.path != 1 || false == tool.atend)
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
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<std::pair<TTrack *, editor_track::state>> states;
|
||||
std::vector<TTrack *> created;
|
||||
auto const linked{link_tracks(target, link, states, created)};
|
||||
bool const changed{false == states.empty() || false == created.empty()};
|
||||
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);
|
||||
}
|
||||
|
||||
std::vector<TTrack *> editor_mode::link_tracks(editor_track::snap_target const &Target, editor_track::branch_link const &Link, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created)
|
||||
{
|
||||
auto *target{Target.track};
|
||||
if (Link.trim > 0.0)
|
||||
{
|
||||
States.emplace_back(target, editor_track::capture(*target));
|
||||
auto const run{glm::length(plan_of(Target.direction))};
|
||||
editor_track::move_point(*target, Target.point, Target.position + Target.direction * (Link.trim / std::max(run, 1e-6)));
|
||||
editor_track::commit({target});
|
||||
}
|
||||
std::vector<TTrack *> pieces;
|
||||
for (auto const &piece : Link.pieces)
|
||||
pieces.push_back(editor_track::create_path(*target, piece));
|
||||
editor_track::commit(pieces);
|
||||
digitize_drawn(pieces);
|
||||
Created.insert(Created.end(), pieces.begin(), pieces.end());
|
||||
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, double const Radius, TTrack &Style, std::string const &Type)
|
||||
{
|
||||
auto const at = [](glm::dvec2 const &Planar, double const Y) { return glm::dvec3{Planar.x, Y, Planar.y}; };
|
||||
|
||||
Reference in New Issue
Block a user