mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-13 08:20:59 +01:00
feat(editor): the drive of a switch stands by the tips of its blades, not at the joint before them, and goes with the switch deleted. A drive placed, changed or carried along with its switch is set where the blades begin, as they're shaped or where the diverging track is broken at the tip of a planed blade, along the main track there, its heading and its tilt taken there; the drives the earlier versions of the editor set at the joints are moved there once, as the scenery is opened and the templates are known, with no step of the undo history, to be saved with it. Deleting a switch, a double switch or a set of tracks takes the drives of the switches along in the same step, Ctrl+Z brings them back. The joint before the blades and the tips of the blades of the switch being placed or the one selected are marked across the track in the view, so the tips seen on the photo aren't taken for the start of the switch. Selftests bladetips, drivetips; on a copy of test42 the drive of z1 went from the joint to 1.248 m, deleted with z1, back with Ctrl+Z
This commit is contained in:
@@ -1346,6 +1346,21 @@ bool editor_mode::update()
|
||||
{
|
||||
Global.editor_tracks = (current_work_area() == work_area::tracks);
|
||||
selftest_step();
|
||||
// the drives of the switches go to the tips of their blades once, as the templates of the scenery are known
|
||||
if (false == m_drives_checked)
|
||||
{
|
||||
EditorIncludes.start();
|
||||
if (EditorIncludes.scanned())
|
||||
{
|
||||
m_drives_checked = true;
|
||||
if (auto const moved{drives_to_blade_tips()}; moved > 0)
|
||||
{
|
||||
auto const text{format(STR_P("The drive of %zu switch is moved to the tips of its blades", "The drives of %zu switches are moved to the tips of their blades", static_cast<long long>(moved)), moved)};
|
||||
ui()->set_status(text, false);
|
||||
WriteLog("Editor: " + text, logtype::generic);
|
||||
}
|
||||
}
|
||||
}
|
||||
// after the tool has told its own status
|
||||
if (false == m_orphan_note.empty())
|
||||
{
|
||||
@@ -1560,6 +1575,7 @@ bool editor_mode::update()
|
||||
draw_track_set();
|
||||
draw_track_spread();
|
||||
draw_build_overlay();
|
||||
draw_blade_tips();
|
||||
draw_track_intent();
|
||||
draw_tape();
|
||||
m_speed.update();
|
||||
|
||||
@@ -1714,6 +1714,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
bool extend_snap(editor_track::snap_target &Target) const;
|
||||
void update_build_tools();
|
||||
void draw_build_overlay() const;
|
||||
// the joint before the blades of the switch being placed or the one selected and the tips of its blades
|
||||
void draw_blade_tips() const;
|
||||
// modes of the track editor, one at a time: path is the plain selection
|
||||
using track_tab = editor_track_tab;
|
||||
struct track_mode
|
||||
@@ -2089,6 +2091,12 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
void apply_switch_drive_choice(TTrack &Switch);
|
||||
void switch_drive_captured(TTrack const &Track);
|
||||
void switch_drives_committed(std::vector<TTrack *> const &Tracks);
|
||||
// the drives of the switches deleted go with them, in the step of the undo history the deletion made last
|
||||
void drives_deleted(std::vector<TTrack *> const &Switches);
|
||||
// the drives of all the switches of the scenery set at the tips of their blades, the ones the earlier versions of the editor set at the
|
||||
// joint before them too: once, as the scenery is opened, with no step of the undo history; how many moved
|
||||
std::size_t drives_to_blade_tips();
|
||||
bool m_drives_checked{false};
|
||||
scene::layer_handle output_layer(std::string const &Part) override;
|
||||
// places them in the active layer or the given one, as one step of the undo. returns: how many were placed
|
||||
std::size_t place_templates(std::vector<template_item> const &Items, std::string &Error, scene::layer_handle Layer = scene::layer_handle{}) override;
|
||||
|
||||
@@ -50,6 +50,28 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
editor_mode::selftest_result editor_mode::selftest_switch(std::string const &command, std::istringstream &words)
|
||||
{
|
||||
// each switch: the tips of its blades and its drive from the joint before them; drivetips moves the drives there first
|
||||
if (command == "bladetips" || command == "drivetips")
|
||||
{
|
||||
if (command == "drivetips")
|
||||
WriteLog(format("SELFTEST drivetips: %zu moved", drives_to_blade_tips()));
|
||||
for (auto *track : simulation::Paths.sequence())
|
||||
{
|
||||
if (track == nullptr || track->m_editorremoved || track->eType != tt_Switch || track->m_paths.size() < 2)
|
||||
continue;
|
||||
auto const start{track->m_paths[0].points[segment_data::point::start]};
|
||||
std::string text{format("SELFTEST drive of %s: blades from %.3f m", track->name().c_str(), editor_track::blade_tip_of(*track))};
|
||||
for (int path = 0; path < 2; ++path)
|
||||
if (auto const &shape{track->blade(path)}; shape.valid)
|
||||
text += format(", blade %d %.3f - %.3f m", path, shape.tip, shape.heel);
|
||||
if (auto const found{find_switch_drive(*track)})
|
||||
text += format(", drive %.3f m from the joint", geometry::plan_distance(found->standing.location, start));
|
||||
else
|
||||
text += ", no drive";
|
||||
WriteLog(text);
|
||||
}
|
||||
return selftest_result::handled;
|
||||
}
|
||||
if (command == "curvedswitch")
|
||||
{
|
||||
std::string name;
|
||||
|
||||
@@ -292,6 +292,41 @@ bool editor_mode::replace_switch_drive(TTrack &Switch, int const Family, bool co
|
||||
return true;
|
||||
}
|
||||
|
||||
void editor_mode::drives_deleted(std::vector<TTrack *> const &Switches)
|
||||
{
|
||||
if (m_history.empty())
|
||||
return;
|
||||
for (auto const *track : Switches)
|
||||
{
|
||||
if (track == nullptr || track->eType != tt_Switch)
|
||||
continue;
|
||||
auto const found{find_switch_drive(*track)};
|
||||
if (false == found.has_value() || false == scene::Layers.removable(found->standing.instance) || scene::Layers.instance(found->standing.instance).removed)
|
||||
continue;
|
||||
scene::Layers.removed(found->standing.instance, true);
|
||||
m_history.back().instances.push_back(found->standing.instance);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t editor_mode::drives_to_blade_tips()
|
||||
{
|
||||
// the drives the earlier versions of the editor set at the joint before the blades among them
|
||||
std::vector<TTrack *> switches;
|
||||
std::vector<std::pair<scene::instance_handle, std::string>> before;
|
||||
for (auto *track : simulation::Paths.sequence())
|
||||
{
|
||||
if (track == nullptr || track->m_editorremoved || track->eType != tt_Switch)
|
||||
continue;
|
||||
if (auto const found{find_switch_drive(*track)}; found.has_value() && scene::Layers.removable(found->standing.instance))
|
||||
{
|
||||
switches.push_back(track);
|
||||
before.emplace_back(found->standing.instance, scene::Layers.directive(found->standing.instance));
|
||||
}
|
||||
}
|
||||
switch_drives_committed(switches);
|
||||
return static_cast<std::size_t>(std::count_if(before.begin(), before.end(), [](auto const &Before) { return scene::Layers.directive(Before.first) != Before.second; }));
|
||||
}
|
||||
|
||||
bool editor_mode::render_switch_drive_choice()
|
||||
{
|
||||
auto &tool{m_switch};
|
||||
|
||||
@@ -1641,6 +1641,7 @@ void editor_mode::delete_selected_track()
|
||||
if (auto const note{designs_trimmed({set->first, set->link, set->second})}; false == note.empty())
|
||||
ui()->set_status(note, false);
|
||||
push_track_snapshot({}, created, {set->first, set->link, set->second});
|
||||
drives_deleted({set->first, set->second});
|
||||
if (std::any_of(m_route.chain.tracks.begin(), m_route.chain.tracks.end(), [&](TTrack const *Each) { return Each == set->first || Each == set->link || Each == set->second; }))
|
||||
m_route = {};
|
||||
m_node = nullptr;
|
||||
@@ -1666,6 +1667,7 @@ void editor_mode::delete_selected_track()
|
||||
if (auto const note{designs_trimmed({track})}; false == note.empty())
|
||||
ui()->set_status(note, false);
|
||||
push_track_snapshot({}, created, {track});
|
||||
drives_deleted({track});
|
||||
// the model and the events of a turntable can't stay without its track
|
||||
if (track->eType == tt_Table)
|
||||
for (auto const instance : turntable_includes(*track))
|
||||
@@ -2264,6 +2266,53 @@ void editor_mode::draw_track_hints()
|
||||
foreground->AddText(at, IM_COL32(255, 255, 255, 255), readout.c_str());
|
||||
}
|
||||
|
||||
void editor_mode::draw_blade_tips() const
|
||||
{
|
||||
segment_data main;
|
||||
double tip{0.0};
|
||||
if (m_switch.placing && m_switch.connection < 0)
|
||||
{
|
||||
auto const preview{switch_preview()};
|
||||
if (preview.size() < 2)
|
||||
return;
|
||||
main = preview[0];
|
||||
tip = editor_track::blade_tip_of(preview[1]);
|
||||
}
|
||||
else if (auto const *selected{selected_track()}; selected != nullptr && selected->eType == tt_Switch && selected->m_paths.size() >= 2 && false == selected->m_editorremoved)
|
||||
{
|
||||
main = selected->m_paths[0];
|
||||
tip = editor_track::blade_tip_of(*selected);
|
||||
}
|
||||
else
|
||||
return;
|
||||
// on the photo the tips of the blades are seen where the switch seems to begin, the joint before them isn't
|
||||
screen_projection const projection;
|
||||
auto *drawlist{ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())};
|
||||
geometry::bezier const curve{main};
|
||||
auto const mark = [&](double const Distance, ImU32 const Colour, float const Width, char const *Label, int const Side) {
|
||||
auto const t{curve.parameter(Distance)};
|
||||
auto const at{curve.point(t)};
|
||||
auto direction{plan_of(curve.first(t))};
|
||||
if (glm::length(direction) < 1e-9)
|
||||
return;
|
||||
direction = glm::normalize(direction);
|
||||
glm::dvec3 const across{-direction.y * 1.6, 0.0, direction.x * 1.6};
|
||||
projection.line(drawlist, at - across, at + across, IM_COL32(0, 0, 0, 170), Width + 3.0f);
|
||||
projection.line(drawlist, at - across, at + across, Colour, Width);
|
||||
ImVec2 screen;
|
||||
if (projection.project(at + across * static_cast<double>(Side), screen))
|
||||
{
|
||||
auto const size{ImGui::CalcTextSize(Label)};
|
||||
ImVec2 const corner{Side > 0 ? screen.x + 6.0f : screen.x - 6.0f - size.x, screen.y - 0.5f * size.y};
|
||||
drawlist->AddRectFilled({corner.x - 3.0f, corner.y - 1.0f}, {corner.x + size.x + 3.0f, corner.y + size.y + 1.0f}, IM_COL32(0, 0, 0, 160), 3.0f);
|
||||
drawlist->AddText(corner, Colour, Label);
|
||||
}
|
||||
};
|
||||
mark(0.0, IM_COL32(220, 220, 220, 230), 2.0f, STR_C("joint before the blades"), -1);
|
||||
if (tip > 0.05)
|
||||
mark(tip, IM_COL32(255, 170, 40, 255), 3.0f, STR_C("tips of the blades"), 1);
|
||||
}
|
||||
|
||||
void editor_mode::draw_build_overlay() const
|
||||
{
|
||||
screen_projection const projection;
|
||||
|
||||
@@ -1831,6 +1831,7 @@ void editor_mode::track_set_delete()
|
||||
}
|
||||
auto const designs{designs_trimmed(removed)};
|
||||
push_track_snapshot({}, {}, removed);
|
||||
drives_deleted(removed);
|
||||
m_track_set.clear();
|
||||
m_node = nullptr;
|
||||
ui()->set_node(nullptr);
|
||||
|
||||
@@ -208,7 +208,12 @@ class editor_track
|
||||
glm::dvec2 tilt{0.0};
|
||||
bool rightswitch{false};
|
||||
};
|
||||
// the drive of the switch by the tips of its blades, where the rods throw them over
|
||||
static std::optional<drive_spot> drive_spot_of(TTrack const &Switch);
|
||||
// how far along the main track of the switch the tips of its blades lie, where the pre-blade stretch from the joint ends: as the
|
||||
// blades are shaped, else where the diverging track is broken at the tip of a blade planed back from the start; 0 at the start
|
||||
static double blade_tip_of(TTrack const &Switch);
|
||||
static double blade_tip_of(segment_data const &Diverging);
|
||||
|
||||
struct straight_axis
|
||||
{
|
||||
|
||||
@@ -299,6 +299,26 @@ glm::dvec3 editor_track::control_vector(TTrack const &Track, point_ref const &Co
|
||||
return point_position(Track, Control) - point_position(Track, end_of(Control));
|
||||
}
|
||||
|
||||
double editor_track::blade_tip_of(segment_data const &Diverging)
|
||||
{
|
||||
// the tip of a planed blade lies some metres from the start at most, a longer first piece is a part of the curve
|
||||
if (Diverging.pieces.size() < 2)
|
||||
return 0.0;
|
||||
auto const length{bezier{Diverging.pieces.front()}.plan_length()};
|
||||
return length < 5.0 ? length : 0.0;
|
||||
}
|
||||
|
||||
double editor_track::blade_tip_of(TTrack const &Switch)
|
||||
{
|
||||
if (Switch.eType != tt_Switch || Switch.m_paths.size() < 2)
|
||||
return 0.0;
|
||||
double tip{0.0};
|
||||
for (int path = 0; path < 2; ++path)
|
||||
if (auto const &shape{Switch.blade(path)}; shape.valid && shape.tip > 0.0)
|
||||
tip = tip > 0.0 ? std::min(tip, shape.tip) : shape.tip;
|
||||
return tip > 0.0 ? tip : blade_tip_of(Switch.m_paths[1]);
|
||||
}
|
||||
|
||||
std::optional<editor_track::drive_spot> editor_track::drive_spot_of(TTrack const &Switch)
|
||||
{
|
||||
if (Switch.eType != tt_Switch || Switch.m_paths.size() < 2)
|
||||
@@ -306,8 +326,10 @@ std::optional<editor_track::drive_spot> editor_track::drive_spot_of(TTrack const
|
||||
auto const &main{Switch.m_paths[0]};
|
||||
auto const &diverging{Switch.m_paths[1]};
|
||||
auto const start{main.points[segment_data::point::start]};
|
||||
auto const &control{main.points[segment_data::point::control1]};
|
||||
auto const along{control != glm::dvec3{} ? control : main.points[segment_data::point::end] - start};
|
||||
// by the tips of the blades, not at the joint before them
|
||||
bezier const curve{main};
|
||||
auto const at{curve.parameter(blade_tip_of(Switch))};
|
||||
auto const along{curve.first(at)};
|
||||
auto heading{plan_of(along)};
|
||||
if (glm::length(heading) < 1e-9)
|
||||
return std::nullopt;
|
||||
@@ -315,11 +337,11 @@ std::optional<editor_track::drive_spot> editor_track::drive_spot_of(TTrack const
|
||||
glm::dvec2 const right{-heading.y, heading.x};
|
||||
drive_spot spot;
|
||||
spot.rightswitch = glm::dot(plan_of(diverging.points[segment_data::point::end] - start), right) > 0.0;
|
||||
spot.location = start;
|
||||
spot.location = curve.point(at);
|
||||
auto const yaw{glm::degrees(std::atan2(heading.x, heading.y))};
|
||||
spot.yaw = yaw < 0.0 ? yaw + 360.0 : yaw;
|
||||
auto const run{glm::length(plan_of(along))};
|
||||
auto const roll{static_cast<double>(main.rolls[0])};
|
||||
auto const roll{glm::mix(static_cast<double>(main.rolls[0]), static_cast<double>(main.rolls[1]), at)};
|
||||
spot.tilt = {-glm::degrees(std::atan(along.y / run)), roll};
|
||||
if (Global.bRollFix)
|
||||
spot.location.y += 0.75 * std::abs(std::sin(glm::radians(roll)));
|
||||
|
||||
@@ -12303,3 +12303,15 @@ msgid_plural "The designs kept for the lines are cut down to the tracks left alo
|
||||
msgstr[0] "Projekt linii zapisany w scenerii przycięto do torów, które przy nim zostały"
|
||||
msgstr[1] "Projekty linii zapisane w scenerii przycięto do torów, które przy nich zostały"
|
||||
msgstr[2] "Projekty linii zapisane w scenerii przycięto do torów, które przy nich zostały"
|
||||
|
||||
msgid "joint before the blades"
|
||||
msgstr "styk przediglicowy"
|
||||
|
||||
msgid "tips of the blades"
|
||||
msgstr "ostrza iglic"
|
||||
|
||||
msgid "The drive of %zu switch is moved to the tips of its blades"
|
||||
msgid_plural "The drives of %zu switches are moved to the tips of their blades"
|
||||
msgstr[0] "Napęd %zu rozjazdu przestawiony na ostrza iglic"
|
||||
msgstr[1] "Napędy %zu rozjazdów przestawione na ostrza iglic"
|
||||
msgstr[2] "Napędy %zu rozjazdów przestawione na ostrza iglic"
|
||||
|
||||
Reference in New Issue
Block a user