17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 01:07:28 +01:00

feat(editor): the kept alignments and switches after a drag of the gizmo or of a point: a line all of whose paths are moved or turned as a whole takes its kept design along; paths pulled out of a kept design, or given another cant, are told about on the status line with the way back; a switch moved or turned as a whole keeps its type and its record follows it, with its km; a switch bent out of the shape of its type loses it and is told about (//$r records keep the shape of the switch); all of it in the same undo step as the drag; Polish translation

This commit is contained in:
maj00r
2026-10-11 16:57:23 +02:00
parent ddb637a3f5
commit 4ea2f4f13f
8 changed files with 307 additions and 10 deletions

View File

@@ -607,6 +607,49 @@ void forget(int const Index)
mark(layer);
}
std::optional<hit> on_record(int const Index, glm::dvec3 const &Point, double const Reach)
{
load();
if (Index < 0 || Index >= static_cast<int>(g_records.size()))
return std::nullopt;
return project(static_cast<std::size_t>(Index), plan_of(Point), Reach);
}
double cant_at(int const Index, double const Chainage)
{
load();
if (Index < 0 || Index >= static_cast<int>(g_records.size()))
return 0.0;
return alignment::evaluate(g_records[Index].result, Chainage).cant;
}
void transform(int const Index, glm::dvec3 const &From, glm::dvec2 const &Fromdirection, glm::dvec3 const &To, glm::dvec2 const &Todirection)
{
load();
if (Index < 0 || Index >= static_cast<int>(g_records.size()))
return;
auto &entry{g_records[Index]};
auto const angle{geometry::signed_angle(glm::normalize(Fromdirection), glm::normalize(Todirection))};
auto const lift{To.y - From.y};
auto const place = [&](glm::dvec3 const &Point) {
auto const planar{plan_of(To) + geometry::turned(plan_of(Point) - plan_of(From), angle)};
return glm::dvec3{planar.x, Point.y + lift, planar.y};
};
auto &design{entry.design};
design.start = place(design.start);
design.end = place(design.end);
design.start_direction = geometry::turned(design.start_direction, angle);
design.end_direction = geometry::turned(design.end_direction, angle);
for (auto &vertex : design.vertices)
vertex.position = plan_of(place({vertex.position.x, 0.0, vertex.position.y}));
finish(entry);
entry.elements.clear();
for (auto const &e : entry.result.elements)
entry.elements.push_back(describe(e));
g_changed = true;
mark(entry.layer);
}
std::optional<double> chainage_at(glm::dvec3 const &Point)
{
auto const found{at(Point)};

View File

@@ -85,6 +85,12 @@ int departed(std::vector<TTrack *> const &Tracks, double &Departure);
// keeps the design in the file of the layer, in place of the one with the same ends
int store(alignment::design const &Design, alignment::result const &Result, double const Origin, scene::layer_handle const Layer);
void forget(int const Index);
// the point on the line of the stored alignment, if it lies by it within the reach
std::optional<hit> on_record(int const Index, glm::dvec3 const &Point, double const Reach);
// cant of the design at the chainage, mm
double cant_at(int const Index, double const Chainage);
// the stored alignment moved and turned in the plan the way the point and the direction were
void transform(int const Index, glm::dvec3 const &From, glm::dvec2 const &Fromdirection, glm::dvec3 const &To, glm::dvec2 const &Todirection);
// km of the point on the stored alignments, if it lies on one
std::optional<double> chainage_at(glm::dvec3 const &Point);
// the records as they were before the changes made since the last call, if there were any; the current ones become the base

View File

@@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/.
#include "editor/editorSwitchStore.hpp"
#include "editor/editorFormat.hpp"
#include "editor/editorGeometry.hpp"
#include "editor/editorAlignmentStore.hpp"
#include "world/Track.h"
#include "simulation/simulation.h"
@@ -39,7 +40,8 @@ bool g_loaded{false};
// format of the lines, coordinates in the space of the file:
// <switch|slip|crossing> <name> <type> <rail> <R> <n of 1:n> <hand L|R|-> <a> <b> <R of the main track, + right, - left, 0 straight>
// parts <count> <names>... start <x y z> dir <x z> km <km|-> number <label|-> source <designed|measured>
// parts <count> <names>... shape <main end along, across> <diverging end along, across> start <x y z> dir <x z>
// km <km|-> number <label|-> source <designed|measured>
std::string word(std::string const &Text)
{
if (Text.empty())
@@ -69,6 +71,32 @@ bool in_place(record const &Record, TTrack const &Track)
return geometry::plan_distance(Track.m_paths.front().points[segment_data::point::start], Record.start) <= kTolerance && glm::dot(direction_of(Track), Record.direction) > 0.9999;
}
glm::dvec4 measure(TTrack const &Track)
{
auto const &main{Track.m_paths.front()};
auto const start{plan_of(main.points[segment_data::point::start])};
auto const along{direction_of(Track)};
glm::dvec2 const across{-along.y, along.x};
auto const local = [&](glm::dvec3 const &Point) {
auto const offset{plan_of(Point) - start};
return glm::dvec2{glm::dot(offset, along), glm::dot(offset, across)};
};
auto const mainend{local(main.points[segment_data::point::end])};
auto const branchend{Track.m_paths.size() > 1 ? local(Track.m_paths[1].points[segment_data::point::end]) : glm::dvec2{0.0}};
return {mainend.x, mainend.y, branchend.x, branchend.y};
}
bool in_shape(record const &Record, TTrack const &Track)
{
if (false == Record.shaped)
return in_place(Record, Track);
auto const now{measure(Track)};
for (int i = 0; i < 4; ++i)
if (std::abs(now[i] - Record.shape[i]) > kTolerance)
return false;
return true;
}
void mark(scene::layer_handle const Layer)
{
if (false == scene::Layers.valid(Layer))
@@ -89,6 +117,8 @@ void mark(scene::layer_handle const Layer)
entry.parts.size())};
for (auto const &part : entry.parts)
text += ' ' + word(part);
if (entry.shaped)
text += format(" shape %.4f %.4f %.4f %.4f", entry.shape.x, entry.shape.y, entry.shape.z, entry.shape.w);
text += format(" start %.4f %.4f %.4f dir %.9f %.9f km ", start.x, start.y, start.z, direction.x, direction.y);
text += entry.km.has_value() ? format("%.3f", *entry.km / 1000.0) : std::string{"-"};
text += " number " + word(entry.number) + " source " + word(entry.source);
@@ -135,7 +165,13 @@ void load()
line >> part;
glm::dvec3 start{0.0}, ahead{0.0};
glm::dvec2 direction{0.0, 1.0};
line >> keyword >> start.x >> start.y >> start.z >> keyword >> direction.x >> direction.y >> keyword >> km >> keyword >> entry.number >> keyword >> entry.source;
line >> keyword;
if (keyword == "shape")
{
line >> entry.shape.x >> entry.shape.y >> entry.shape.z >> entry.shape.w >> keyword;
entry.shaped = true;
}
line >> start.x >> start.y >> start.z >> keyword >> direction.x >> direction.y >> keyword >> km >> keyword >> entry.number >> keyword >> entry.source;
if (line.fail())
continue;
entry.start = context.to_world(start);
@@ -159,23 +195,51 @@ std::vector<record> const &records()
return g_records;
}
std::optional<record> of(TTrack const &Track)
std::optional<record> named(TTrack const &Track)
{
load();
auto const &name{Track.name()};
if (name.empty())
return std::nullopt;
for (auto const &entry : g_records)
{
if (std::find(entry.parts.begin(), entry.parts.end(), name) == entry.parts.end())
continue;
auto const *first{usable(entry.parts.front())};
if (first != nullptr && in_place(entry, *first))
if (std::find(entry.parts.begin(), entry.parts.end(), name) != entry.parts.end())
return entry;
}
return std::nullopt;
}
std::optional<record> of(TTrack const &Track)
{
auto const found{named(Track)};
if (false == found.has_value())
return std::nullopt;
auto const *first{usable(found->parts.front())};
if (first == nullptr || false == in_shape(*found, *first))
return std::nullopt;
return found;
}
std::vector<std::string> follow()
{
load();
std::vector<std::string> moved;
std::set<scene::layer_handle> layers;
for (auto &entry : g_records)
{
auto const *first{usable(entry.parts.front())};
if (first == nullptr || false == entry.shaped || false == in_shape(entry, *first) || in_place(entry, *first))
continue;
entry.start = first->m_paths.front().points[segment_data::point::start];
entry.direction = direction_of(*first);
entry.km = alignment_store::chainage_at(entry.start);
moved.push_back(entry.name);
layers.insert(entry.layer);
}
for (auto const layer : layers)
mark(layer);
g_changed = g_changed || false == moved.empty();
return moved;
}
double branch_radius(record const &Record)
{
if (Record.radius <= 0.0)
@@ -188,6 +252,11 @@ double branch_radius(record const &Record)
void put(record Entry, scene::layer_handle const Layer)
{
load();
if (auto const *first{usable(Entry.parts.empty() ? std::string{} : Entry.parts.front())})
{
Entry.shape = measure(*first);
Entry.shaped = true;
}
auto const existing{std::find_if(g_records.begin(), g_records.end(), [&](record const &Other) { return Other.name == Entry.name && Other.kind == Entry.kind; })};
if (existing != g_records.end())
{
@@ -222,6 +291,7 @@ void forget(std::string const &Name)
void tidy()
{
load();
follow();
std::set<scene::layer_handle> layers;
for (auto &entry : g_records)
{

View File

@@ -38,6 +38,10 @@ struct record
std::vector<std::string> parts; // the switch, the switches of a slip, the paths of a crossing
glm::dvec3 start{0.0}; // of the first part
glm::dvec2 direction{0.0, 1.0};
// ends of the main and the diverging track of the first part seen from its start: along, across, along, across. it stays the
// same as long as the switch is moved or turned as a whole, it changes when the switch gets bent out of its shape
glm::dvec4 shape{0.0};
bool shaped{false};
std::optional<double> km;
std::string number{"-"};
std::string source{"designed"}; // designed: from the catalogue, measured: copied from a switch of the scenery
@@ -46,8 +50,12 @@ struct record
void reset();
void load();
std::vector<record> const &records();
// the record of the switch, slip or crossing the track is a part of, while its first part starts where the record says
// the record of the switch, slip or crossing the track is a part of, while its first part keeps the shape the record says
std::optional<record> of(TTrack const &Track);
// the record of the track by its name alone, also when the switch lost its shape
std::optional<record> named(TTrack const &Track);
// records whose switches were moved or turned as a whole take their new place and km; returns their names
std::vector<std::string> follow();
// radius of the diverging track: of the type, bent with the main track for an arc switch
double branch_radius(record const &Record);
// keeps the record in the file of the layer, in place of the one with the same name