mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-31 09:59:06 +01:00
feat(editor): a switch laid into a curve is checked by ST-T1-A6 as it's placed: the radii of its main and diverging tracks at its start and its end shown at it; warnings where the curvature of a track turns from one side to the other within it (KZW II, 9.7 pkt 12), where the diverging track is sharper or the main track bent more than table 11.9 allows the basic switch (the values allowed by way of exception named), where a switch of 1:9.403 or steeper is bent (11.4 pkt 4), where the cant changes within it (11.4 pkt 7), where a cant ramp starts or ends within it or under 6 m off it (9.6 pkt 16-17) and where the cant is over 100 mm (table 9.6); a note on a transition curve, which only an exception allows. The ends of the cant ramps are marked on the curve, and the start of the switch pressed within 3 m of where it starts or ends at an end of a ramp, or keeps the 6 m off it, is drawn there. The warnings stay in the status once it's laid. The checks stand on their own in editor/editorSwitchCurve, with unit tests
This commit is contained in:
@@ -35,6 +35,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "editor/editorAlignment.hpp"
|
||||
#include "editor/editorAlignmentStore.hpp"
|
||||
#include "editor/editorSwitchStore.hpp"
|
||||
#include "editor/editorSwitchCurve.hpp"
|
||||
#include "editor/editorKilometrage.hpp"
|
||||
#include "editor/editorSpeed.hpp"
|
||||
#include "editor/editorRoad.hpp"
|
||||
@@ -1240,6 +1241,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
int last{0}; // template armed the last time
|
||||
bool placing{false};
|
||||
double along{0.0};
|
||||
double pressed{0.0}; // along a curve: where the press was, the start of the switch drawn from it to the ends of the cant ramps
|
||||
glm::dvec3 point{0.0};
|
||||
glm::dvec3 mouse{0.0};
|
||||
editor_track::straight line;
|
||||
@@ -1528,6 +1530,15 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
void switch_placed(std::vector<TTrack *> const &Created);
|
||||
static std::vector<curve_sample> sample_chain(editor_track::chain const &Chain);
|
||||
static curve_sample sample_at(std::vector<curve_sample> const &Frame, double const Station);
|
||||
// the curve a switch goes into as the norms look at it: its headings and its cant along it
|
||||
std::vector<switch_curve::track_point> frame_points(std::vector<curve_sample> const &Frame) const;
|
||||
// the way the switch being placed into a curve goes along it and the side its diverging track leaves to, from the drag
|
||||
void curved_switch_pose(int &Direction, int &Side) const;
|
||||
// the start of the switch being placed into a curve: where it was pressed, drawn to where the switch meets an end of a cant ramp or
|
||||
// keeps 6 m off it; What receives which
|
||||
double curved_switch_start(int const Direction, std::string *What = nullptr) const;
|
||||
// the switch made of the paths, laid into the curve at the station, as ST-T1-A6 sees it
|
||||
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 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);
|
||||
void insert_curved_switch(int const Direction, int const Side);
|
||||
|
||||
@@ -51,12 +51,10 @@ std::vector<segment_data> editor_mode::switch_preview() const
|
||||
return {};
|
||||
if (tool.curved)
|
||||
{
|
||||
auto const sample{sample_at(tool.frame, tool.along)};
|
||||
glm::dvec2 const offset{tool.mouse.x - tool.point.x, tool.mouse.z - tool.point.z};
|
||||
auto const forward{glm::dot(offset, sample.tangent) >= 0.0 ? 1 : -1};
|
||||
glm::dvec2 const direction{sample.tangent * static_cast<double>(forward)};
|
||||
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);
|
||||
int forward;
|
||||
int side;
|
||||
curved_switch_pose(forward, side);
|
||||
return curved_switch_paths(tool.templates[tool.armed], tool.frame, curved_switch_start(forward), forward, side);
|
||||
}
|
||||
if (switch_link plan; plan_switch_link(plan))
|
||||
{
|
||||
@@ -116,6 +114,7 @@ bool editor_mode::start_switch_placement()
|
||||
return false;
|
||||
tool.curved = true;
|
||||
tool.along = nearest_station(tool.frame, ground);
|
||||
tool.pressed = tool.along;
|
||||
tool.point = sample_at(tool.frame, tool.along).position;
|
||||
if (plan_distance(tool.point, ground) > kSwitchReach)
|
||||
return false;
|
||||
@@ -167,11 +166,10 @@ void editor_mode::finish_switch_placement()
|
||||
}
|
||||
if (tool.curved)
|
||||
{
|
||||
auto const sample{sample_at(tool.frame, tool.along)};
|
||||
glm::dvec2 const offset{tool.mouse.x - tool.point.x, tool.mouse.z - tool.point.z};
|
||||
auto const forward{glm::dot(offset, sample.tangent) >= 0.0 ? 1 : -1};
|
||||
glm::dvec2 const direction{sample.tangent * static_cast<double>(forward)};
|
||||
auto const side{direction.x * offset.y - direction.y * offset.x >= 0.0 ? 1 : -1};
|
||||
int forward;
|
||||
int side;
|
||||
curved_switch_pose(forward, side);
|
||||
tool.along = curved_switch_start(forward);
|
||||
insert_curved_switch(forward, side);
|
||||
return;
|
||||
}
|
||||
@@ -1256,6 +1254,89 @@ double editor_mode::nearest_station(std::vector<curve_sample> const &Frame, glm:
|
||||
return station;
|
||||
}
|
||||
|
||||
std::vector<switch_curve::track_point> editor_mode::frame_points(std::vector<curve_sample> const &Frame) const
|
||||
{
|
||||
auto const gauge{m_route.design.norms.gauge};
|
||||
std::vector<switch_curve::track_point> points;
|
||||
for (auto const &sample : Frame)
|
||||
{
|
||||
switch_curve::track_point point;
|
||||
point.station = sample.station;
|
||||
point.heading = std::atan2(sample.tangent.y, sample.tangent.x);
|
||||
// the headings go on through a half turn, so that their differences are the turns between them
|
||||
if (false == points.empty())
|
||||
point.heading = points.back().heading + std::remainder(point.heading - points.back().heading, 2.0 * glm::pi<double>());
|
||||
point.cant = gauge * std::sin(glm::radians(static_cast<double>(sample.roll)));
|
||||
points.push_back(point);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
void editor_mode::curved_switch_pose(int &Direction, int &Side) const
|
||||
{
|
||||
auto const &tool{m_switch};
|
||||
auto const sample{sample_at(tool.frame, tool.pressed)};
|
||||
glm::dvec2 const offset{tool.mouse.x - tool.point.x, tool.mouse.z - tool.point.z};
|
||||
Direction = glm::dot(offset, sample.tangent) >= 0.0 ? 1 : -1;
|
||||
glm::dvec2 const direction{sample.tangent * static_cast<double>(Direction)};
|
||||
Side = direction.x * offset.y - direction.y * offset.x >= 0.0 ? 1 : -1;
|
||||
}
|
||||
|
||||
double editor_mode::curved_switch_start(int const Direction, std::string *What) const
|
||||
{
|
||||
auto const &tool{m_switch};
|
||||
if (What != nullptr)
|
||||
What->clear();
|
||||
if (tool.frame.size() < 2 || tool.armed < 0 || tool.armed >= static_cast<int>(tool.templates.size()))
|
||||
return tool.pressed;
|
||||
auto const length{tool.templates[tool.armed].length};
|
||||
std::string what;
|
||||
auto const start{switch_curve::snapped_start(switch_curve::ramp_ends(frame_points(tool.frame)), tool.pressed, length, Direction, 3.0, &what)};
|
||||
// a start which would put the switch off the curve isn't one
|
||||
auto const total{tool.frame.back().station};
|
||||
if (Direction > 0 ? start < 0.0 || start > total - length : start < length || start > total)
|
||||
return tool.pressed;
|
||||
if (What != nullptr)
|
||||
*What = what;
|
||||
return start;
|
||||
}
|
||||
|
||||
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};
|
||||
if (Paths.size() < 2 || tool.armed < 0 || tool.armed >= static_cast<int>(tool.templates.size()))
|
||||
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]};
|
||||
return switch_curve::check(along(Paths[0]), along(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)
|
||||
{
|
||||
auto const map = [&](glm::dvec3 const &Local, glm::dvec2 &Tangent, glm::dvec2 const &Localtangent) {
|
||||
@@ -1393,10 +1474,16 @@ void editor_mode::insert_curved_switch(int const Direction, int const Side)
|
||||
}
|
||||
}
|
||||
auto const paths{curved_switch_paths(shape, tool.frame, station, Direction, Side)};
|
||||
auto const report{curved_switch_report(paths, station, Direction)};
|
||||
auto const made{create_switch_set(shape, paths, *tool.frame_tracks.front(), switch_name_for_new())};
|
||||
created.insert(created.end(), made.begin(), made.end());
|
||||
if (made.size() == 1)
|
||||
tool.status = format(STR_C("Switch %s inserted into the curve, Ctrl+Z takes it back"), made.front()->name().c_str());
|
||||
// what the norms say of it stays in the status
|
||||
if (report.transition)
|
||||
tool.status += "\n" + report.notes.front();
|
||||
for (auto const &warning : report.warnings)
|
||||
tool.status += "\n" + warning;
|
||||
auto const placed{created};
|
||||
push_track_snapshot(std::move(states), std::move(created), std::move(removed));
|
||||
straight_refresh();
|
||||
|
||||
@@ -2354,8 +2354,64 @@ void editor_mode::draw_build_overlay() const
|
||||
}
|
||||
else if (m_switch.placing)
|
||||
{
|
||||
for (auto const &piece : switch_preview())
|
||||
auto const preview{switch_preview()};
|
||||
for (auto const &piece : preview)
|
||||
drawpath(piece, overlay_color::marked);
|
||||
// into a curve: the ends of the cant ramps, what the start was drawn to, the radii at the ends of the switch and the norms
|
||||
if (auto const &tool{m_switch}; tool.curved && tool.connection < 0 && preview.size() >= 2)
|
||||
{
|
||||
auto const points{frame_points(tool.frame)};
|
||||
for (auto const end : switch_curve::ramp_ends(points))
|
||||
{
|
||||
auto const sample{sample_at(tool.frame, end)};
|
||||
glm::dvec3 const across{-sample.tangent.y * 2.5, 0.0, sample.tangent.x * 2.5};
|
||||
projection.line(drawlist, sample.position - across, sample.position + across, IM_COL32(255, 170, 40, 255), 3.0f);
|
||||
ImVec2 screen;
|
||||
if (projection.project(sample.position + across, screen))
|
||||
drawlist->AddText({screen.x + 4.0f, screen.y - 7.0f}, IM_COL32(255, 170, 40, 255), STR_C("ramp"));
|
||||
}
|
||||
int forward;
|
||||
int side;
|
||||
curved_switch_pose(forward, side);
|
||||
std::string snapped;
|
||||
auto const 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); };
|
||||
std::vector<std::pair<std::string, ImU32>> lines;
|
||||
if (false == snapped.empty())
|
||||
lines.emplace_back(snapped, IM_COL32(255, 170, 40, 255));
|
||||
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));
|
||||
for (auto const ¬e : report.notes)
|
||||
lines.emplace_back(note, IM_COL32(170, 200, 230, 235));
|
||||
for (auto const &warning : report.warnings)
|
||||
lines.emplace_back(warning, overlay_color::invalid);
|
||||
ImVec2 screen;
|
||||
if (projection.project(sample_at(tool.frame, station).position, screen))
|
||||
{
|
||||
auto const wrap{460.0f};
|
||||
auto height{0.0f};
|
||||
auto width{0.0f};
|
||||
for (auto const &[text, colour] : lines)
|
||||
{
|
||||
auto const size{ImGui::CalcTextSize(text.c_str(), nullptr, false, wrap)};
|
||||
height += size.y + 2.0f;
|
||||
width = std::max(width, size.x);
|
||||
}
|
||||
ImVec2 corner{screen.x + 24.0f, screen.y + 24.0f};
|
||||
drawlist->AddRectFilled({corner.x - 6.0f, corner.y - 4.0f}, {corner.x + width + 6.0f, corner.y + height + 4.0f}, IM_COL32(10, 20, 30, 215), 4.0f);
|
||||
for (auto const &[text, colour] : lines)
|
||||
{
|
||||
drawlist->AddText(ImGui::GetFont(), ImGui::GetFontSize(), corner, colour, text.c_str(), nullptr, wrap);
|
||||
corner.y += ImGui::CalcTextSize(text.c_str(), nullptr, false, wrap).y + 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch_link plan;
|
||||
auto const linked{plan_switch_link(plan)};
|
||||
ImVec2 screen;
|
||||
|
||||
Reference in New Issue
Block a user