mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-31 23:59:03 +01:00
feat(editor): the curves of the laying from the photo in the design of the lines: a vertex of two transition curves meeting at its radius, with no arc and no cant, kept in the scenery files; the bends of straights eased with them at a radius from 6000 down to 3000 m, or larger where they'd be too short for the speed, else with an arc of the norms as large as fits, the straights between two bends shared, the bends staying where they were clicked and a curve below the norms kept with its warnings; a stretch traced on the photo fitted with a straight, a curve with its radius and the length of its transitions, a compound one, or two curves with a straight between them, on a thread of its own while the cursor moves; the heights from point to point through all the points clicked. Unit tests of the design of the lines
This commit is contained in:
@@ -371,4 +371,86 @@ void follow_meanwhile(std::vector<segment_data> &Pieces, ends const &Ends, bed_q
|
||||
lay_on(Pieces, lengths, total, Ends, stretched ? &graded : nullptr, stretched ? last.total / total : 1.0);
|
||||
}
|
||||
|
||||
void follow_points(std::vector<segment_data> &Pieces, std::vector<glm::dvec3> const &Points, ends const &Ends)
|
||||
{
|
||||
std::vector<double> lengths;
|
||||
double total{0.0};
|
||||
for (auto const &piece : Pieces)
|
||||
total += lengths.emplace_back(bezier{piece}.plan_length(256));
|
||||
if (total < 1e-3)
|
||||
return;
|
||||
// the plan of the pieces as a line of short chords, each point of it with its chainage
|
||||
std::vector<glm::dvec2> line;
|
||||
std::vector<double> chainages;
|
||||
{
|
||||
double from{0.0};
|
||||
for (std::size_t i = 0; i < Pieces.size(); ++i)
|
||||
{
|
||||
bezier const curve{Pieces[i]};
|
||||
auto const steps{std::max(4, static_cast<int>(std::ceil(lengths[i] / kStep)))};
|
||||
for (int j = (i == 0 ? 0 : 1); j <= steps; ++j)
|
||||
{
|
||||
line.push_back(plan_of(curve.point(curve.parameter(lengths[i] * j / steps))));
|
||||
chainages.push_back(from + lengths[i] * j / steps);
|
||||
}
|
||||
from += lengths[i];
|
||||
}
|
||||
}
|
||||
auto const chainage_of = [&](glm::dvec2 const &Point) {
|
||||
auto best{std::numeric_limits<double>::max()};
|
||||
double chainage{0.0};
|
||||
for (std::size_t i = 0; i + 1 < line.size(); ++i)
|
||||
{
|
||||
auto const chord{line[i + 1] - line[i]};
|
||||
auto const length2{glm::dot(chord, chord)};
|
||||
auto const t{length2 > 1e-18 ? std::clamp(glm::dot(Point - line[i], chord) / length2, 0.0, 1.0) : 0.0};
|
||||
auto const offset{Point - (line[i] + chord * t)};
|
||||
if (auto const distance2{glm::dot(offset, offset)}; distance2 < best)
|
||||
{
|
||||
best = distance2;
|
||||
chainage = chainages[i] + (chainages[i + 1] - chainages[i]) * t;
|
||||
}
|
||||
}
|
||||
return chainage;
|
||||
};
|
||||
// the knots: the ends, and between them the points in their order along the track, none too near the one before
|
||||
std::vector<double> knots{0.0};
|
||||
std::vector<double> heights{Ends.height0};
|
||||
for (auto const &point : Points)
|
||||
{
|
||||
auto const chainage{chainage_of(plan_of(point))};
|
||||
if (chainage < knots.back() + 1.0 || chainage > total - 1.0)
|
||||
continue;
|
||||
knots.push_back(chainage);
|
||||
heights.push_back(point.y);
|
||||
}
|
||||
knots.push_back(total);
|
||||
heights.push_back(Ends.height1);
|
||||
auto const last{knots.size() - 1};
|
||||
// the height at the chainage on the stretch Middle is on, an end of it taken at a knot
|
||||
auto const at = [&](double const Chainage, double const Middle, double &Grade) {
|
||||
std::size_t stretch{0};
|
||||
while (stretch + 1 < last && Middle > knots[stretch + 1])
|
||||
++stretch;
|
||||
auto const span{knots[stretch + 1] - knots[stretch]};
|
||||
auto const even{(heights[stretch + 1] - heights[stretch]) / span};
|
||||
auto const from{stretch == 0 && false == std::isnan(Ends.grade0) ? Ends.grade0 : even};
|
||||
auto const to{stretch + 1 == last && false == std::isnan(Ends.grade1) ? Ends.grade1 : even};
|
||||
// a cubic of Hermite, a straight grade where both ends keep the even one
|
||||
auto const t{std::clamp((Chainage - knots[stretch]) / span, 0.0, 1.0)};
|
||||
auto const t2{t * t};
|
||||
auto const t3{t2 * t};
|
||||
Grade = ((6.0 * t2 - 6.0 * t) * heights[stretch] + (3.0 * t2 - 4.0 * t + 1.0) * from * span + (6.0 * t - 6.0 * t2) * heights[stretch + 1] + (3.0 * t2 - 2.0 * t) * to * span) / span;
|
||||
return (2.0 * t3 - 3.0 * t2 + 1.0) * heights[stretch] + (t3 - 2.0 * t2 + t) * from * span + (3.0 * t2 - 2.0 * t3) * heights[stretch + 1] + (t3 - t2) * to * span;
|
||||
};
|
||||
std::vector<std::pair<double, double>> spans;
|
||||
auto result{cut(Pieces, lengths, std::vector<double>(knots.begin() + 1, knots.end() - 1), spans)};
|
||||
for (std::size_t i = 0; i < result.size(); ++i)
|
||||
{
|
||||
auto const middle{(spans[i].first + spans[i].second) * 0.5};
|
||||
lift(result[i], spans[i], [&](double const Chainage, double &Grade) { return at(Chainage, middle, Grade); });
|
||||
}
|
||||
Pieces = std::move(result);
|
||||
}
|
||||
|
||||
} // namespace ground_follow
|
||||
|
||||
Reference in New Issue
Block a user