From de5f2069f78e52518bfc2895909a8bff29ddd696 Mon Sep 17 00:00:00 2001 From: maj00r Date: Sat, 3 Oct 2026 00:55:29 +0200 Subject: [PATCH] feat(editor): dragging straight refits neighbor curves --- application/editormode.h | 5 ++ application/editormodetrack.cpp | 101 ++++++++++++++++++++++++++++++-- editor/editorTrack.cpp | 27 +++++++++ editor/editorTrack.hpp | 2 + 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/application/editormode.h b/application/editormode.h index 02cbd544..7c56b5e3 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -340,6 +340,9 @@ class editor_mode : public application_mode editor_track::straight drag_line; std::vector set; std::vector drag_lines; + bool fitting{false}; + editor_track::curve fit_before; + editor_track::curve fit_after; std::vector> drag_states; glm::dvec3 preview_start{0.0}; glm::dvec3 preview_end{0.0}; @@ -381,6 +384,8 @@ class editor_mode : public application_mode double straight_tool_radius(editor_track::straight const &Line) const; void straight_reshape(editor_track::straight const &Line, double const From, double const To, std::function const &Tail, double const Radius); void straight_refresh(); + bool start_curve_fit(editor_track::straight const &Line); + void update_curve_fit(glm::dvec3 const &Start, glm::dvec3 const &End); void add_detour_point(); void apply_detour(); std::vector detour_outline() const; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 586a6fc8..f3a10879 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -1474,7 +1474,22 @@ void editor_mode::draw_straights_overlay() const projection.line(drawlist, state.tool_point + along * length + shifted, line.end + shifted, IM_COL32(255, 210, 60, 255), 3.0f); } } - if (m_straights.dragging) + if (m_straights.dragging && m_straights.fitting) + { + auto const &result{m_route.result}; + if (result.length > 0.0 && result.profile.size() >= 2) + { + int const steps{std::clamp(static_cast(result.length / 2.0), 2, 2000)}; + auto previous{alignment::evaluate(result, 0.0).position}; + for (int i = 1; i <= steps; ++i) + { + auto const next{alignment::evaluate(result, result.length * i / steps).position}; + projection.line(drawlist, previous, next, result.valid ? IM_COL32(255, 210, 60, 255) : IM_COL32(255, 60, 60, 255), 3.0f); + previous = next; + } + } + } + else if (m_straights.dragging) { for (auto const &member : m_straights.drag_lines) { @@ -1911,15 +1926,19 @@ void editor_mode::render_straight_gizmo() { lines.push_back(line); } - auto const tracks{editor_track::straight_affected(lines)}; + state.fitting = lines.size() == 1 && start_curve_fit(line); + auto const tracks{state.fitting ? std::vector{} : editor_track::straight_affected(lines)}; state.dragging = true; state.drag_line = line; state.drag_lines = lines; state.drag_states.clear(); for (auto *track : tracks) state.drag_states.emplace_back(track, editor_track::capture(*track)); - push_track_snapshot(state.drag_states); - m_track_drag = tracks; + if (false == state.fitting) + { + push_track_snapshot(state.drag_states); + m_track_drag = tracks; + } } auto const &grabbed{state.drag_line}; glm::dvec3 moved{camerapos + glm::dvec3(state.gizmo[3])}; @@ -1955,6 +1974,11 @@ void editor_mode::render_straight_gizmo() ends.emplace_back(transform(member.start), transform(member.end)); state.preview_start = transform(grabbed.start); state.preview_end = transform(grabbed.end); + if (state.fitting) + { + update_curve_fit(state.preview_start, state.preview_end); + return; + } for (auto const &entry : state.drag_states) editor_track::apply(*entry.first, entry.second); editor_track::move_straights(state.drag_lines, ends); @@ -1963,6 +1987,16 @@ void editor_mode::render_straight_gizmo() } else if (state.dragging) { + if (state.fitting) + { + state.fitting = false; + state.dragging = false; + state.drag_lines.clear(); + if (m_route.result.valid) + route_apply(); + straight_refresh(); + return; + } commit_track_drag(true); state.dragging = false; m_track_drag.clear(); @@ -2898,3 +2932,62 @@ void editor_mode::apply_detour() route_apply(); straight_refresh(); } + +bool editor_mode::start_curve_fit(editor_track::straight const &Line) +{ + auto &state{m_straights}; + auto *before{editor_track::outside_neighbour(Line, false)}; + auto *after{editor_track::outside_neighbour(Line, true)}; + if (before == nullptr || after == nullptr) + return false; + if (false == editor_track::find_curve(*before, state.tolerance, m_route.design.norms.gauge, state.fit_before) || false == editor_track::find_curve(*after, state.tolerance, m_route.design.norms.gauge, state.fit_after)) + return false; + if (state.fit_before.reversals != 0 || state.fit_after.reversals != 0) + return false; + auto const far = [](editor_track::curve const &Curve, glm::dvec3 const &Joint) { return editor_track::touches(*Curve.from, Joint) ? Curve.to : Curve.from; }; + m_route.from = far(state.fit_before, Line.start); + m_route.to = far(state.fit_after, Line.end); + route_reset(); + if (m_route.chain.tracks.empty()) + return false; + return true; +} + +void editor_mode::update_curve_fit(glm::dvec3 const &Start, glm::dvec3 const &End) +{ + auto &state{m_straights}; + auto &design{m_route.design}; + glm::dvec2 const point{Start.x, Start.z}; + glm::dvec2 const direction{glm::normalize(glm::dvec2{End.x - Start.x, End.z - Start.z})}; + auto const cross = [](glm::dvec2 const &A, glm::dvec2 const &B) { return A.x * B.y - A.y * B.x; }; + glm::dvec2 const start{design.start.x, design.start.z}; + glm::dvec2 const end{design.end.x, design.end.z}; + auto const startdirection{glm::normalize(design.start_direction)}; + auto const enddirection{glm::normalize(design.end_direction)}; + auto const first{cross(point - start, direction) / cross(startdirection, direction)}; + auto const last{cross(end - point, direction) / cross(enddirection, direction)}; + auto const make = [&](editor_track::curve const &Curve, double const Offset) { + alignment::vertex vertex; + vertex.radius = Curve.radius > 0.0 ? std::round(Curve.radius) : 1000.0; + route_recommend(vertex); + if (Curve.transition_in > 0.0) + vertex.transition_in = std::round(Curve.transition_in); + if (Curve.transition_out > 0.0) + vertex.transition_out = std::round(Curve.transition_out); + if (Curve.cant > 0.0) + vertex.cant = std::round(Curve.cant); + if (Curve.compound) + { + vertex.compound = true; + vertex.radius2 = std::round(Curve.radius2); + vertex.transition_middle = std::round(Curve.transition_middle); + vertex.split = Curve.split; + } + vertex.offset = std::max(0.1, Offset); + return vertex; + }; + auto before{make(state.fit_before, first)}; + auto after{make(state.fit_after, last)}; + design.vertices = {before, after}; + route_update(); +} diff --git a/editor/editorTrack.cpp b/editor/editorTrack.cpp index d6e80d05..2ecfc9e8 100644 --- a/editor/editorTrack.cpp +++ b/editor/editorTrack.cpp @@ -1253,6 +1253,33 @@ TTrack *editor_track::create_switch(switch_template const &Template, std::vector return track; } +bool editor_track::touches(TTrack const &Track, glm::dvec3 const &Point) +{ + for (auto const &path : Track.m_paths) + for (auto const index : {segment_data::point::start, segment_data::point::end}) + if (glm::distance(path.points[index], Point) <= kSamePoint) + return true; + return false; +} + +TTrack *editor_track::outside_neighbour(straight const &Line, bool const Atend) +{ + auto const &point{Atend ? Line.end : Line.start}; + for (std::size_t i = 0; i < Line.tracks.size(); ++i) + { + auto const &path{Line.tracks[i]->m_paths[Line.paths[i]]}; + for (auto const end : {false, true}) + { + if (glm::distance(path.points[end ? segment_data::point::end : segment_data::point::start], point) > kSamePoint) + continue; + auto *neighbour{neighbour_at(*Line.tracks[i], Line.paths[i], end)}; + if (neighbour != nullptr && std::find(Line.tracks.begin(), Line.tracks.end(), neighbour) == Line.tracks.end()) + return neighbour; + } + } + return nullptr; +} + std::vector editor_track::straight_affected(std::vector const &Lines) { std::vector result; diff --git a/editor/editorTrack.hpp b/editor/editorTrack.hpp index f1b535a1..297d217b 100644 --- a/editor/editorTrack.hpp +++ b/editor/editorTrack.hpp @@ -118,6 +118,8 @@ class editor_track static straight find_straight(TTrack &Track, straight_tolerance const &Tolerance); static std::vector find_straights(double const Minimumlength, straight_tolerance const &Tolerance); static std::vector straight_affected(std::vector const &Lines); + static TTrack *outside_neighbour(straight const &Line, bool const Atend); + static bool touches(TTrack const &Track, glm::dvec3 const &Point); struct curve { TTrack *from{nullptr};