17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 01:39:20 +02:00

feat(editor): curves take over the adjoining straights

This commit is contained in:
maj00r
2026-10-04 00:14:03 +02:00
parent 676bf39136
commit cdd5c242d1
6 changed files with 297 additions and 38 deletions

View File

@@ -277,11 +277,18 @@ recommendation recommend(double const Speed, double const Radius, limits const &
return result;
}
result compute(design const &Design)
namespace
{
result fit_between(design const &Design)
{
result r;
r.shape = Design.shape;
r.gauge = Design.norms.gauge;
r.start = Design.start;
r.end = Design.end;
auto const startreserve{std::max(0.0, Design.start_reserve)};
auto const endreserve{std::max(0.0, Design.end_reserve)};
auto const start{plan(Design.start)};
auto const end{plan(Design.end)};
@@ -351,6 +358,16 @@ result compute(design const &Design)
straight.length = Length;
push(straight);
};
auto const starts_early = [&](double const Straight) {
if (Straight < 0.0)
r.start_extension = -Straight;
if (Straight >= -1e-4 || -Straight <= startreserve + 1e-4)
return;
if (startreserve > 0.0)
r.errors.emplace_back(format("Vertex 1: the curve starts %.2f m before the adjoining straight", -Straight - startreserve));
else
r.errors.emplace_back(format("Vertex 1: the curve starts %.2f m before the fixed start", -Straight));
};
struct fitted
{
@@ -441,9 +458,9 @@ result compute(design const &Design)
std::vector<double> scales(count, 1.0);
for (std::size_t leg = 0; leg + 1 < polygon.size(); ++leg)
{
auto const available{glm::distance(polygon[leg], polygon[leg + 1])};
auto const available{glm::distance(polygon[leg], polygon[leg + 1]) + (leg == 0 ? startreserve : 0.0) + (leg == count ? endreserve : 0.0)};
auto const required{(leg > 0 ? fits[leg - 1].tangent_out : 0.0) + (leg < count ? fits[leg].tangent_in : 0.0)};
if (required <= available)
if (required <= available + 1e-6)
continue;
auto const scale{available / required * (1.0 - 1e-9)};
if (leg > 0)
@@ -534,13 +551,10 @@ result compute(design const &Design)
auto const along{cross(directionout, endoffset) / cross(directionout, directionin)};
auto const curvestart{position - directionin * along};
auto const straight{glm::dot(curvestart - cursor, directionin)};
if (straight < -1e-4)
{
if (k == 0)
r.errors.emplace_back(format("Vertex 1: the curve starts %.2f m before the fixed start", -straight));
else
r.errors.emplace_back(format("Curves of vertices %zu and %zu overlap by %.2f m", k, k + 1, -straight));
}
if (k == 0)
starts_early(straight);
else if (straight < -1e-4)
r.errors.emplace_back(format("Curves of vertices %zu and %zu overlap by %.2f m", k, k + 1, -straight));
push_straight(cursor, directionin, straight);
auto const startchainage{chainage};
glm::dvec2 origin{curvestart};
@@ -615,13 +629,10 @@ result compute(design const &Design)
}
auto const straight{glm::dot(curvestart - cursor, directionin)};
if (straight < -1e-4)
{
if (k == 0)
r.errors.emplace_back(format("Vertex 1: the curve starts %.2f m before the fixed start", -straight));
else
r.errors.emplace_back(format("Curves of vertices %zu and %zu overlap by %.2f m", k, k + 1, -straight));
}
if (k == 0)
starts_early(straight);
else if (straight < -1e-4)
r.errors.emplace_back(format("Curves of vertices %zu and %zu overlap by %.2f m", k, k + 1, -straight));
push_straight(cursor, directionin, straight);
element curve;
@@ -698,8 +709,15 @@ result compute(design const &Design)
}
auto const lastdirection{glm::normalize(polygon.back() - polygon[polygon.size() - 2])};
auto const tail{glm::dot(end - cursor, lastdirection)};
if (tail < -1e-4)
r.errors.emplace_back(format("Vertex %zu: the curve ends %.2f m past the fixed end", count, -tail));
if (tail < 0.0)
r.end_extension = -tail;
if (tail < -1e-4 && -tail > endreserve + 1e-4)
{
if (endreserve > 0.0)
r.errors.emplace_back(format("Vertex %zu: the curve ends %.2f m past the adjoining straight", count, -tail - endreserve));
else
r.errors.emplace_back(format("Vertex %zu: the curve ends %.2f m past the fixed end", count, -tail));
}
push_straight(cursor, lastdirection, tail);
r.length = chainage;
@@ -757,6 +775,39 @@ result compute(design const &Design)
return r;
}
glm::dvec3 along_end(glm::dvec3 const &Point, glm::dvec2 const &Direction, double const Grade, double const Distance)
{
auto const direction{glm::normalize(Direction)};
return {Point.x + direction.x * Distance, Point.y + Grade * Distance, Point.z + direction.y * Distance};
}
} // namespace
result compute(design const &Design)
{
auto first{fit_between(Design)};
auto const startextension{first.start_extension > 1e-4 ? first.start_extension : 0.0};
auto const endextension{first.end_extension > 1e-4 ? first.end_extension : 0.0};
if (false == first.errors.empty() || (startextension == 0.0 && endextension == 0.0))
{
first.start_extension = first.end_extension = 0.0;
return first;
}
auto extended{Design};
extended.start = along_end(Design.start, Design.start_direction, Design.start_grade, -startextension);
extended.end = along_end(Design.end, Design.end_direction, Design.end_grade, endextension);
extended.start_reserve = extended.end_reserve = 0.0;
if (extended.vertices.size() >= 2)
{
extended.vertices.front().offset += startextension;
extended.vertices.back().offset += endextension;
}
auto r{fit_between(extended)};
r.start_extension = startextension;
r.end_extension = endextension;
return r;
}
bool tangent_intersection(design const &Design, glm::dvec2 &Point)
{
auto const start{plan(Design.start)};

View File

@@ -65,6 +65,9 @@ struct design
double end_grade{0.0};
double start_radius{0.0};
double end_radius{0.0};
// length of the straight beyond the start and the end, which the curves can take over
double start_reserve{0.0};
double end_reserve{0.0};
std::vector<vertex> vertices;
transition_shape shape{transition_shape::cubic_parabola};
double speed{100.0};
@@ -152,6 +155,10 @@ struct result
std::vector<curve_report> curves;
std::vector<profile_point> profile;
double length{0.0};
glm::dvec3 start{0.0};
glm::dvec3 end{0.0};
double start_extension{0.0};
double end_extension{0.0};
};
result compute(design const &Design);

View File

@@ -27,6 +27,7 @@ http:
#include <numeric>
#include <functional>
#include <sstream>
#include <tuple>
#include <unordered_set>
namespace
@@ -1179,6 +1180,121 @@ bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance
return true;
}
namespace
{
// shorter remainders of a straight path are absorbed by moving the joint instead of splitting the path
double split_margin(double const Length)
{
return std::max(0.5, 0.002 * Length);
}
double plan_length(segment_data const &Path)
{
return glm::length(plan_of(Path.points[segment_data::point::end] - Path.points[segment_data::point::start]));
}
} // namespace
editor_track::straight_run editor_track::straight_beyond(chain const &Chain, bool const Atend, straight_tolerance const &Tolerance)
{
straight_run run;
if (Chain.tracks.empty())
return run;
auto *edge{Atend ? Chain.tracks.back() : Chain.tracks.front()};
bool const forward{Atend ? Chain.forward.back() : Chain.forward.front()};
TTrack *current{forward == Atend ? edge->trNext : edge->trPrev};
auto joint{Atend ? Chain.end : Chain.start};
auto const origin{plan_of(joint)};
auto direction{glm::normalize(plan_of(Atend ? Chain.end_direction : -Chain.start_direction))};
while (current != nullptr && run.tracks.size() < 1000)
{
std::string reason;
if (current->eType != tt_Normal || current->m_editorremoved || false == can_edit_geometry(*current, reason))
break;
if (std::find(Chain.tracks.begin(), Chain.tracks.end(), current) != Chain.tracks.end() || std::find(run.tracks.begin(), run.tracks.end(), current) != run.tracks.end())
break;
auto const &path{current->m_paths.front()};
if (false == plan_straight(path, Tolerance))
break;
bool outward;
if (glm::distance(path.points[segment_data::point::start], joint) <= kSamePoint)
outward = true;
else if (glm::distance(path.points[segment_data::point::end], joint) <= kSamePoint)
outward = false;
else
break;
auto const &far{path.points[outward ? segment_data::point::end : segment_data::point::start]};
if (run.tracks.empty())
{
auto const own{glm::normalize(plan_of(far - joint))};
if (std::abs(lateral_of(direction, own)) > 0.01 || glm::dot(direction, own) <= 0.0)
break;
direction = own;
}
auto const offset{plan_of(far) - origin};
auto const along{glm::dot(offset, direction)};
if (along <= run.length + 1e-3 || std::abs(lateral_of(direction, offset)) > Tolerance.offset)
break;
run.tracks.push_back(current);
run.outward.push_back(outward);
run.length = along;
joint = far;
current = outward ? current->trNext : current->trPrev;
}
return run;
}
double editor_track::run_reserve(straight_run const &Run)
{
if (Run.tracks.empty())
return 0.0;
return std::max(0.0, Run.length - split_margin(plan_length(Run.tracks.back()->m_paths.front())) - 0.01);
}
TTrack *editor_track::take_straight(straight_run const &Run, TTrack *Edge, glm::dvec3 const &Joint, glm::dvec3 const &Cut, std::vector<std::pair<TTrack *, state>> &States, std::vector<TTrack *> &Created)
{
auto const length{glm::length(plan_of(Cut - Joint))};
auto *edge{Edge};
double covered{0.0};
for (std::size_t i = 0; i < Run.tracks.size(); ++i)
{
auto *track{Run.tracks[i]};
auto const outward{Run.outward[i]};
auto const &path{track->m_paths.front()};
auto const pathlength{plan_length(path)};
auto const margin{split_margin(pathlength)};
auto const remaining{length - covered};
if (remaining <= margin)
{
if (std::abs(remaining) > 1e-4)
{
States.emplace_back(track, capture(*track));
move_point(*track, {0, outward ? point_kind::start : point_kind::end}, Cut);
commit({track});
}
break;
}
if (remaining < pathlength - margin)
{
auto const &nearpoint{path.points[outward ? segment_data::point::start : segment_data::point::end]};
auto const &farpoint{path.points[outward ? segment_data::point::end : segment_data::point::start]};
auto const t{nearest_parameter(*track, nearpoint + (farpoint - nearpoint) * (remaining / pathlength))};
auto const before{capture(*track)};
auto *created{split_path(*track, t)};
if (created == nullptr)
break;
States.emplace_back(track, before);
Created.push_back(created);
edge = outward ? track : created;
break;
}
edge = track;
covered += pathlength;
}
return edge;
}
std::vector<editor_track::switch_template> editor_track::find_switch_templates()
{
std::vector<switch_template> result;

View File

@@ -137,6 +137,16 @@ class editor_track
double split{0.5};
};
static bool find_curve(TTrack &Track, straight_tolerance const &Tolerance, double const Gauge, curve &Curve);
// regular straight paths continuing a chain end in line, which the chain can take over
struct straight_run
{
std::vector<TTrack *> tracks;
std::vector<bool> outward;
double length{0.0};
};
static straight_run straight_beyond(chain const &Chain, bool const Atend, straight_tolerance const &Tolerance);
static double run_reserve(straight_run const &Run);
static TTrack *take_straight(straight_run const &Run, TTrack *Edge, glm::dvec3 const &Joint, glm::dvec3 const &Cut, std::vector<std::pair<TTrack *, state>> &States, std::vector<TTrack *> &Created);
struct switch_template
{
TTrack *source{nullptr};