mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-30 22:30:28 +01:00
feat(editor): the drawn tracks kept in the scenery as //$h lines as they are drawn: laying, pulling out of a free end, joining two ends and the parallel track record the whole line they belong to, its straights, arcs and transition curves, with the radii from the chord and the turn of the arcs and the transitions from the turn they make, in the same undo step; the curves read in the route window and the Curve tab are what the paths are, no transitions or cant made up for them; a curve ends at the point of reverse; a parallel arc is concentric with its source; the paths laid in pieces of up to 90 degrees match the kept design within their own approximation; 'Keep the line as it lies' for lines drawn before; self-test commands digitize and parallel; Polish translation
This commit is contained in:
@@ -382,10 +382,23 @@ std::vector<record> const &records()
|
||||
namespace
|
||||
{
|
||||
|
||||
// the point on the line of the stored alignment, if it lies by it within the reach and not beyond its ends
|
||||
std::optional<hit> project(std::size_t const Index, glm::dvec2 const &Point, double const Reach)
|
||||
double approximation(geometry::bezier const &Curve)
|
||||
{
|
||||
auto const &entry{g_records[Index]};
|
||||
auto const begin{plan_of(Curve.first(0.0))};
|
||||
auto const finish{plan_of(Curve.first(1.0))};
|
||||
if (glm::length(begin) < 1e-9 || glm::length(finish) < 1e-9)
|
||||
return 0.0;
|
||||
auto const turn{std::abs(geometry::signed_angle(glm::normalize(begin), glm::normalize(finish)))};
|
||||
if (turn < 1e-6)
|
||||
return 0.0;
|
||||
auto const radius{geometry::plan_distance(Curve.point(0.0), Curve.point(1.0)) / (2.0 * std::sin(turn * 0.5))};
|
||||
return 3.0e-4 * radius * std::pow(turn / (0.5 * 3.14159265358979323846), 6.0);
|
||||
}
|
||||
|
||||
// the point on the line of the stored alignment, if it lies by it within the reach and not beyond its ends
|
||||
std::optional<hit> project(record const &Entry, std::size_t const Index, glm::dvec2 const &Point, double const Reach)
|
||||
{
|
||||
auto const &entry{Entry};
|
||||
auto const margin{Reach + kStep};
|
||||
if (entry.samples.empty() || Point.x < entry.low.x - margin || Point.y < entry.low.y - margin || Point.x > entry.high.x + margin || Point.y > entry.high.y + margin)
|
||||
return std::nullopt;
|
||||
@@ -430,6 +443,31 @@ std::optional<hit> project(std::size_t const Index, glm::dvec2 const &Point, dou
|
||||
return hit{static_cast<int>(Index), static_cast<int>(std::max<std::ptrdiff_t>(0, std::distance(elements.begin(), found) - 1)), chainage, offset};
|
||||
}
|
||||
|
||||
std::optional<hit> project(std::size_t const Index, glm::dvec2 const &Point, double const Reach)
|
||||
{
|
||||
return project(g_records[Index], Index, Point, Reach);
|
||||
}
|
||||
|
||||
bool lies_along(record const &Entry, TTrack const &Track)
|
||||
{
|
||||
if (Track.m_paths.empty())
|
||||
return false;
|
||||
geometry::bezier const curve{Track.m_paths.front()};
|
||||
auto const reach{tolerance + approximation(curve)};
|
||||
for (auto const t : {0.0, 0.25, 0.5, 0.75, 1.0})
|
||||
if (false == project(Entry, 0, plan_of(curve.point(t)), t == 0.25 || t == 0.75 ? reach : tolerance).has_value())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
record temporary(alignment::result const &Result)
|
||||
{
|
||||
record entry;
|
||||
entry.result = Result;
|
||||
sample(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<hit> at(glm::dvec3 const &Point, double const Reach)
|
||||
@@ -451,6 +489,7 @@ std::optional<hit> on_track(TTrack const &Track, double const Reach)
|
||||
return std::nullopt;
|
||||
load();
|
||||
geometry::bezier const curve{Track.m_paths.front()};
|
||||
auto const reach{Reach + approximation(curve)};
|
||||
std::optional<hit> best;
|
||||
for (std::size_t r = 0; r < g_records.size(); ++r)
|
||||
{
|
||||
@@ -459,7 +498,7 @@ std::optional<hit> on_track(TTrack const &Track, double const Reach)
|
||||
continue;
|
||||
bool along{true};
|
||||
for (auto const t : {0.0, 0.25, 0.75, 1.0})
|
||||
along = along && project(r, plan_of(curve.point(t)), Reach).has_value();
|
||||
along = along && project(r, plan_of(curve.point(t)), reach).has_value();
|
||||
if (along)
|
||||
best = middle;
|
||||
}
|
||||
@@ -484,9 +523,10 @@ std::optional<designed_vertex> vertex_of(std::vector<std::pair<TTrack *, bool>>
|
||||
if (member.first == nullptr || member.first->m_paths.empty())
|
||||
return std::nullopt;
|
||||
geometry::bezier const curve{member.first->m_paths.front()};
|
||||
auto const reach{tolerance + approximation(curve)};
|
||||
std::vector<hit> hits;
|
||||
for (auto const t : {0.0, 0.25, 0.5, 0.75, 1.0})
|
||||
if (auto const found{project(r, plan_of(curve.point(t)), tolerance)})
|
||||
if (auto const found{project(r, plan_of(curve.point(t)), t == 0.25 || t == 0.75 ? reach : tolerance)})
|
||||
hits.push_back(*found);
|
||||
if (hits.size() < 5)
|
||||
{
|
||||
@@ -559,10 +599,29 @@ int departed(std::vector<TTrack *> const &Tracks, double &Departure)
|
||||
return result;
|
||||
}
|
||||
|
||||
int store(alignment::design const &Design, alignment::result const &Result, double const Origin, scene::layer_handle const Layer)
|
||||
bool follows(alignment::result const &Result, std::vector<TTrack *> const &Tracks)
|
||||
{
|
||||
if (false == Result.valid || Result.elements.empty())
|
||||
return false;
|
||||
auto const entry{temporary(Result)};
|
||||
return std::all_of(Tracks.begin(), Tracks.end(), [&](TTrack const *Track) { return Track != nullptr && lies_along(entry, *Track); });
|
||||
}
|
||||
|
||||
std::optional<double> chainage_on(alignment::result const &Result, glm::dvec3 const &Point)
|
||||
{
|
||||
if (false == Result.valid)
|
||||
return std::nullopt;
|
||||
auto const found{project(temporary(Result), 0, plan_of(Point), tolerance)};
|
||||
if (false == found.has_value())
|
||||
return std::nullopt;
|
||||
return found->chainage;
|
||||
}
|
||||
|
||||
int store(alignment::design const &Design, alignment::result const &Result, double const Origin, scene::layer_handle const Layer, std::string const &Source)
|
||||
{
|
||||
load();
|
||||
record entry;
|
||||
entry.source = Source;
|
||||
entry.design = Design;
|
||||
entry.design.start_reserve = entry.design.end_reserve = 0.0;
|
||||
entry.origin = Origin;
|
||||
|
||||
@@ -83,7 +83,9 @@ int find(glm::dvec3 const &Start, glm::dvec3 const &End, bool &Reversed);
|
||||
// stored alignment near the tracks, whose line they no longer follow; Departure receives the largest distance
|
||||
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);
|
||||
int store(alignment::design const &Design, alignment::result const &Result, double const Origin, scene::layer_handle const Layer, std::string const &Source = "designed");
|
||||
bool follows(alignment::result const &Result, std::vector<TTrack *> const &Tracks);
|
||||
std::optional<double> chainage_on(alignment::result const &Result, glm::dvec3 const &Point);
|
||||
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);
|
||||
|
||||
@@ -1590,6 +1590,17 @@ bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance
|
||||
|
||||
std::deque<std::pair<TTrack *, bool>> run{{&Track, true}};
|
||||
auto const known = [&](TTrack const *Other) { return std::any_of(run.begin(), run.end(), [&](auto const &Member) { return Member.first == Other; }); };
|
||||
auto const own_turn = [](TTrack const &Other, bool const Forward) {
|
||||
auto const &path{Other.m_paths.front()};
|
||||
auto const begin{plan_of(path_tangent(path, false))};
|
||||
auto const finish{plan_of(path_tangent(path, true))};
|
||||
if (glm::length(begin) < 1e-9 || glm::length(finish) < 1e-9)
|
||||
return 0.0;
|
||||
auto const turn{signed_angle(glm::normalize(begin), glm::normalize(finish))};
|
||||
return Forward ? turn : -turn;
|
||||
};
|
||||
auto const side{own_turn(Track, true)};
|
||||
auto const reversing = [&](TTrack const &Other, bool const Forward) { return own_turn(Other, Forward) * side < 0.0 && std::abs(own_turn(Other, Forward)) > 1e-4; };
|
||||
for (auto const throughend : {true, false})
|
||||
{
|
||||
TTrack *previous{&Track};
|
||||
@@ -1603,7 +1614,7 @@ bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance
|
||||
if (atstart == (extension.pNexts[0] == previous))
|
||||
break;
|
||||
auto *beyond{atstart ? extension.pNexts[0] : extension.pPrevs[0]};
|
||||
if (false == curved(beyond) || known(beyond))
|
||||
if (false == curved(beyond) || known(beyond) || reversing(*current, throughend == atstart))
|
||||
break;
|
||||
if (throughend)
|
||||
run.emplace_back(current, atstart);
|
||||
@@ -1616,6 +1627,8 @@ bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance
|
||||
if (false == curved(current))
|
||||
break;
|
||||
auto const entersatstart{current->trPrev == previous};
|
||||
if (reversing(*current, throughend == entersatstart))
|
||||
break;
|
||||
if (throughend)
|
||||
run.emplace_back(current, entersatstart);
|
||||
else
|
||||
@@ -1813,7 +1826,7 @@ bool editor_track::find_run(TTrack &Track, double const Limit, chain &Chain, std
|
||||
return find_chain(ends[1], ends[0], Chain, Error);
|
||||
}
|
||||
|
||||
std::vector<editor_track::line_vertex> editor_track::recognize_line(chain const &Chain, straight_tolerance const &Tolerance, double const Gauge)
|
||||
std::vector<editor_track::line_vertex> editor_track::recognize_line(chain const &Chain, straight_tolerance const &Tolerance, double const Gauge, alignment::transition_shape const Shape)
|
||||
{
|
||||
std::vector<line_vertex> result;
|
||||
auto const count{Chain.tracks.size()};
|
||||
@@ -1844,6 +1857,36 @@ std::vector<editor_track::line_vertex> editor_track::recognize_line(chain const
|
||||
for (auto i = First; i <= Last; ++i)
|
||||
run.emplace_back(Chain.tracks[i], Chain.forward[i]);
|
||||
analyse_curve(run, Gauge, vertex.shape);
|
||||
auto &shape{vertex.shape};
|
||||
if (false == shape.designed.has_value() && false == shape.compound && shape.reversals == 0)
|
||||
{
|
||||
auto const pieces = [&](double const Length, bool const Fromstart) {
|
||||
std::size_t count{0};
|
||||
double covered{0.0};
|
||||
while (covered < Length - 1e-6 && count <= Last - First)
|
||||
{
|
||||
covered += Chain.tracks[Fromstart ? First + count : Last - count]->Length();
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
};
|
||||
auto const in{shape.transition_in > 0.0 ? pieces(shape.transition_in, true) : 0};
|
||||
auto const out{shape.transition_out > 0.0 ? pieces(shape.transition_out, false) : 0};
|
||||
if (in + out <= Last - First)
|
||||
{
|
||||
auto const turn_of = [&](std::size_t const From, std::size_t const To) { return std::abs(std::accumulate(turns.begin() + From, turns.begin() + To + 1, 0.0)); };
|
||||
auto const arc{turn_of(First + in, Last - out)};
|
||||
if (arc > 1e-6 && arc < 2.0 * glm::pi<double>() - 1e-3)
|
||||
{
|
||||
shape.radius = glm::distance(point_in(First + in), point_out(Last - out)) / (2.0 * std::sin(arc * 0.5));
|
||||
auto const length = [&](double const Turn) { return Shape == alignment::transition_shape::clothoid ? 2.0 * shape.radius * Turn : 2.0 * shape.radius * std::tan(Turn); };
|
||||
if (in > 0)
|
||||
shape.transition_in = length(turn_of(First, First + in - 1));
|
||||
if (out > 0)
|
||||
shape.transition_out = length(turn_of(Last - out + 1, Last));
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push_back(vertex);
|
||||
};
|
||||
std::size_t i{0};
|
||||
|
||||
@@ -229,7 +229,7 @@ class editor_track
|
||||
curve shape;
|
||||
bool kink{false}; // the straights meet with no curve between them
|
||||
};
|
||||
static std::vector<line_vertex> recognize_line(chain const &Chain, straight_tolerance const &Tolerance, double const Gauge);
|
||||
static std::vector<line_vertex> recognize_line(chain const &Chain, straight_tolerance const &Tolerance, double const Gauge, alignment::transition_shape const Shape = alignment::transition_shape::cubic_parabola);
|
||||
// regular straight paths continuing a chain end in line, which the chain can take over
|
||||
struct straight_run
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user