17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 03:59:19 +02:00

feat(editor): move straights keeping parallel

This commit is contained in:
maj00r
2026-10-02 22:59:55 +02:00
parent 90f03849c0
commit b986cc8990
4 changed files with 123 additions and 26 deletions

View File

@@ -334,6 +334,8 @@ class editor_mode : public application_mode
int handle{-1}; int handle{-1};
bool dragging{false}; bool dragging{false};
editor_track::straight drag_line; editor_track::straight drag_line;
std::vector<editor_track::straight> set;
std::vector<editor_track::straight> drag_lines;
std::vector<std::pair<TTrack *, editor_track::state>> drag_states; std::vector<std::pair<TTrack *, editor_track::state>> drag_states;
glm::dvec3 preview_start{0.0}; glm::dvec3 preview_start{0.0};
glm::dvec3 preview_end{0.0}; glm::dvec3 preview_end{0.0};
@@ -352,6 +354,8 @@ class editor_mode : public application_mode
bool pick_straight_handle(); bool pick_straight_handle();
void render_straight_gizmo(); void render_straight_gizmo();
void straight_apply(editor_track::straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End); void straight_apply(editor_track::straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End);
void straights_apply(std::vector<editor_track::straight> const &Lines, std::vector<std::pair<glm::dvec3, glm::dvec3>> const &Ends);
bool in_straight_set(editor_track::straight const &Line) const;
void straight_refresh(); void straight_refresh();
straights_state m_straights; straights_state m_straights;
enum class track_tab { straights, route, path }; enum class track_tab { straights, route, path };

View File

@@ -24,6 +24,7 @@ http:
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <functional>
namespace namespace
{ {
@@ -1158,10 +1159,19 @@ void editor_mode::draw_straights_overlay() const
if (nearby(line)) if (nearby(line))
draw(line, i == m_straights.listed ? IM_COL32(40, 220, 255, 255) : IM_COL32(255, 255, 255, 110), i == m_straights.listed ? 4.0f : 2.0f); draw(line, i == m_straights.listed ? IM_COL32(40, 220, 255, 255) : IM_COL32(255, 255, 255, 110), i == m_straights.listed ? 4.0f : 2.0f);
} }
for (auto const &member : m_straights.set)
draw(member, IM_COL32(255, 150, 30, 255), 4.0f);
draw(m_straights.current, IM_COL32(40, 220, 255, 255), 4.0f); draw(m_straights.current, IM_COL32(40, 220, 255, 255), 4.0f);
if (m_straights.dragging) if (m_straights.dragging)
{ {
projection.line(drawlist, m_straights.preview_start, m_straights.preview_end, IM_COL32(255, 210, 60, 255), 3.0f); for (auto const &member : m_straights.drag_lines)
{
for (auto const *track : member.tracks)
{
auto const &path{track->m_paths.front()};
projection.line(drawlist, path.points[segment_data::point::start], path.points[segment_data::point::end], IM_COL32(255, 210, 60, 255), 3.0f);
}
}
} }
else if (false == m_straights.current.tracks.empty()) else if (false == m_straights.current.tracks.empty())
{ {
@@ -1262,6 +1272,16 @@ void editor_mode::render_straights_ui()
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Set as B")) if (ImGui::Button("Set as B"))
state.b = line; state.b = line;
ImGui::SameLine();
if (in_straight_set(line))
{
if (ImGui::Button("Remove from the set"))
state.set.erase(std::remove_if(state.set.begin(), state.set.end(), [&](editor_track::straight const &Member) { return std::any_of(line.tracks.begin(), line.tracks.end(), [&](TTrack const *Track) { return std::find(Member.tracks.begin(), Member.tracks.end(), Track) != Member.tracks.end(); }); }), state.set.end());
}
else if (ImGui::Button("Add to the set"))
{
state.set.push_back(line);
}
if (false == ImGui::IsAnyItemActive()) if (false == ImGui::IsAnyItemActive())
{ {
@@ -1298,6 +1318,14 @@ void editor_mode::render_straights_ui()
if (false == state.status.empty()) if (false == state.status.empty())
ImGui::TextColored(ImVec4(1.0f, 0.35f, 0.3f, 1.0f), "%s", state.status.c_str()); ImGui::TextColored(ImVec4(1.0f, 0.35f, 0.3f, 1.0f), "%s", state.status.c_str());
if (false == state.set.empty())
{
ImGui::Text("Set: %zu straights, dragging any of them moves the whole set, keeping it parallel", state.set.size());
ImGui::SameLine();
if (ImGui::SmallButton("Clear the set"))
state.set.clear();
}
ImGui::Separator(); ImGui::Separator();
auto const &a{state.a}; auto const &a{state.a};
auto const &b{state.b}; auto const &b{state.b};
@@ -1388,13 +1416,30 @@ void editor_mode::straight_refresh()
for (auto &line : state.found) for (auto &line : state.found)
if (false == line.tracks.empty()) if (false == line.tracks.empty())
line = editor_track::find_straight(*line.tracks.front(), state.tolerance); line = editor_track::find_straight(*line.tracks.front(), state.tolerance);
for (auto &line : state.set)
if (false == line.tracks.empty())
line = editor_track::find_straight(*line.tracks.front(), state.tolerance);
}
bool editor_mode::in_straight_set(editor_track::straight const &Line) const
{
for (auto const &member : m_straights.set)
for (auto const *track : Line.tracks)
if (std::find(member.tracks.begin(), member.tracks.end(), track) != member.tracks.end())
return true;
return false;
} }
void editor_mode::straight_apply(editor_track::straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End) void editor_mode::straight_apply(editor_track::straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End)
{
straights_apply({Line}, {{Start, End}});
}
void editor_mode::straights_apply(std::vector<editor_track::straight> const &Lines, std::vector<std::pair<glm::dvec3, glm::dvec3>> const &Ends)
{ {
auto &state{m_straights}; auto &state{m_straights};
state.status.clear(); state.status.clear();
auto const tracks{editor_track::straight_affected(Line)}; auto const tracks{editor_track::straight_affected(Lines)};
for (auto *track : tracks) for (auto *track : tracks)
{ {
if (false == track->Dynamics.empty()) if (false == track->Dynamics.empty())
@@ -1407,7 +1452,7 @@ void editor_mode::straight_apply(editor_track::straight const &Line, glm::dvec3
for (auto *track : tracks) for (auto *track : tracks)
states.emplace_back(track, editor_track::capture(*track)); states.emplace_back(track, editor_track::capture(*track));
push_track_snapshot(std::move(states)); push_track_snapshot(std::move(states));
editor_track::move_straight(Line, Start, End); editor_track::move_straights(Lines, Ends);
editor_track::commit(tracks); editor_track::commit(tracks);
straight_refresh(); straight_refresh();
} }
@@ -1443,39 +1488,68 @@ void editor_mode::render_straight_gizmo()
{ {
if (false == state.dragging) if (false == state.dragging)
{ {
auto const tracks{editor_track::straight_affected(line)}; std::vector<editor_track::straight> lines;
if (in_straight_set(line))
{
for (auto const &member : state.set)
if (false == member.tracks.empty())
lines.push_back(editor_track::find_straight(*member.tracks.front(), state.tolerance));
}
else
{
lines.push_back(line);
}
auto const tracks{editor_track::straight_affected(lines)};
if (std::any_of(tracks.begin(), tracks.end(), [](TTrack const *Track) { return false == Track->Dynamics.empty(); })) if (std::any_of(tracks.begin(), tracks.end(), [](TTrack const *Track) { return false == Track->Dynamics.empty(); }))
{ {
state.status = "There are vehicles placed on the straight or the paths attached to it"; state.status = "There are vehicles placed on the straights or the paths attached to them";
return; return;
} }
state.dragging = true; state.dragging = true;
state.drag_line = line; state.drag_line = line;
state.drag_lines = lines;
state.drag_states.clear(); state.drag_states.clear();
for (auto *track : tracks) for (auto *track : tracks)
state.drag_states.emplace_back(track, editor_track::capture(*track)); state.drag_states.emplace_back(track, editor_track::capture(*track));
push_track_snapshot(state.drag_states); push_track_snapshot(state.drag_states);
m_track_drag = tracks; m_track_drag = tracks;
} }
auto const &original{state.drag_line}; auto const &grabbed{state.drag_line};
glm::dvec3 const moved{camerapos + glm::dvec3(state.gizmo[3])}; glm::dvec3 const moved{camerapos + glm::dvec3(state.gizmo[3])};
auto start{original.start}; std::function<glm::dvec3(glm::dvec3 const &)> transform;
auto end{original.end}; if (state.handle == 2)
if (state.handle == 0) {
start = moved; auto const offset{moved - (grabbed.start + grabbed.end) * 0.5};
else if (state.handle == 1) transform = [offset](glm::dvec3 const &Point) { return Point + offset; };
end = moved; }
else else
{ {
auto const offset{moved - (original.start + original.end) * 0.5}; auto const pivot{state.handle == 0 ? grabbed.end : grabbed.start};
start += offset; auto const original{state.handle == 0 ? grabbed.start : grabbed.end};
end += offset; glm::dvec2 const before{original.x - pivot.x, original.z - pivot.z};
glm::dvec2 const after{moved.x - pivot.x, moved.z - pivot.z};
auto const lengthbefore{glm::length(before)};
auto const lengthafter{std::max(0.1, glm::length(after))};
auto const axis{before / std::max(1e-9, lengthbefore)};
auto const newaxis{after / lengthafter};
auto const scale{lengthafter / std::max(1e-9, lengthbefore)};
auto const rise{moved.y - original.y};
transform = [=](glm::dvec3 const &Point) {
glm::dvec2 const offset{Point.x - pivot.x, Point.z - pivot.z};
auto const along{glm::dot(offset, axis)};
auto const across{axis.x * offset.y - axis.y * offset.x};
auto const planar{glm::dvec2{pivot.x, pivot.z} + newaxis * (along * scale) + glm::dvec2{-newaxis.y, newaxis.x} * across};
return glm::dvec3{planar.x, Point.y + rise * along / std::max(1e-9, lengthbefore), planar.y};
};
} }
state.preview_start = start; std::vector<std::pair<glm::dvec3, glm::dvec3>> ends;
state.preview_end = end; for (auto const &member : state.drag_lines)
ends.emplace_back(transform(member.start), transform(member.end));
state.preview_start = transform(grabbed.start);
state.preview_end = transform(grabbed.end);
for (auto const &entry : state.drag_states) for (auto const &entry : state.drag_states)
editor_track::apply(*entry.first, entry.second); editor_track::apply(*entry.first, entry.second);
editor_track::move_straight(original, start, end); editor_track::move_straights(state.drag_lines, ends);
m_track_dirty = true; m_track_dirty = true;
commit_track_drag(false); commit_track_drag(false);
} }
@@ -1485,6 +1559,7 @@ void editor_mode::render_straight_gizmo()
state.dragging = false; state.dragging = false;
m_track_drag.clear(); m_track_drag.clear();
state.drag_states.clear(); state.drag_states.clear();
state.drag_lines.clear();
straight_refresh(); straight_refresh();
} }
} }

View File

@@ -22,6 +22,7 @@ http:
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <deque> #include <deque>
#include <functional>
#include <sstream> #include <sstream>
#include <unordered_set> #include <unordered_set>
@@ -970,11 +971,15 @@ std::vector<editor_track::straight> editor_track::find_straights(double const Mi
return result; return result;
} }
std::vector<TTrack *> editor_track::straight_affected(straight const &Line) std::vector<TTrack *> editor_track::straight_affected(std::vector<straight> const &Lines)
{ {
std::vector<TTrack *> result(Line.tracks.begin(), Line.tracks.end()); std::vector<TTrack *> result;
auto const member = [&](TTrack const *Track) { return std::find(Line.tracks.begin(), Line.tracks.end(), Track) != Line.tracks.end(); }; for (auto const &line : Lines)
for (auto *track : Line.tracks) for (auto *track : line.tracks)
add_unique(result, track);
auto const members{result};
auto const member = [&](TTrack const *Track) { return std::find(members.begin(), members.end(), Track) != members.end(); };
for (auto *track : members)
{ {
for (int i = 0; i < static_cast<int>(track->m_paths.size()); ++i) for (int i = 0; i < static_cast<int>(track->m_paths.size()); ++i)
{ {
@@ -995,7 +1000,18 @@ std::vector<TTrack *> editor_track::straight_affected(straight const &Line)
return result; return result;
} }
void editor_track::move_straight(straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End) void editor_track::move_straights(std::vector<straight> const &Lines, std::vector<std::pair<glm::dvec3, glm::dvec3>> const &Ends)
{
std::vector<TTrack *> members;
for (auto const &line : Lines)
for (auto *track : line.tracks)
add_unique(members, track);
auto const member = [&](TTrack const *Track) { return std::find(members.begin(), members.end(), Track) != members.end(); };
for (std::size_t i = 0; i < Lines.size() && i < Ends.size(); ++i)
move_straight(Lines[i], Ends[i].first, Ends[i].second, member);
}
void editor_track::move_straight(straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End, std::function<bool(TTrack const *)> const &Member)
{ {
if (Line.tracks.empty() || Line.length < 1e-6) if (Line.tracks.empty() || Line.length < 1e-6)
return; return;
@@ -1053,7 +1069,7 @@ void editor_track::move_straight(straight const &Line, glm::dvec3 const &Start,
return glm::dvec3{moved.x, Point.y - oldheight + newheight, moved.y}; return glm::dvec3{moved.x, Point.y - oldheight + newheight, moved.y};
}; };
auto const member = [&](TTrack const *Track) { return std::find(Line.tracks.begin(), Line.tracks.end(), Track) != Line.tracks.end(); }; auto const &member{Member};
std::vector<std::pair<glm::dvec3, glm::dvec3>> joints; std::vector<std::pair<glm::dvec3, glm::dvec3>> joints;
for (std::size_t i = 0; i < Line.tracks.size(); ++i) for (std::size_t i = 0; i < Line.tracks.size(); ++i)
{ {

View File

@@ -9,6 +9,7 @@ http:
#pragma once #pragma once
#include <functional>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -115,8 +116,8 @@ class editor_track
static bool is_straight(TTrack const &Track, straight_tolerance const &Tolerance); static bool is_straight(TTrack const &Track, straight_tolerance const &Tolerance);
static straight find_straight(TTrack &Track, straight_tolerance const &Tolerance); static straight find_straight(TTrack &Track, straight_tolerance const &Tolerance);
static std::vector<straight> find_straights(double const Minimumlength, straight_tolerance const &Tolerance); static std::vector<straight> find_straights(double const Minimumlength, straight_tolerance const &Tolerance);
static std::vector<TTrack *> straight_affected(straight const &Line); static std::vector<TTrack *> straight_affected(std::vector<straight> const &Lines);
static void move_straight(straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End); static void move_straights(std::vector<straight> const &Lines, std::vector<std::pair<glm::dvec3, glm::dvec3>> const &Ends);
static std::vector<TTrack *> relay(chain const &Chain, std::vector<segment_data> const &Pieces); static std::vector<TTrack *> relay(chain const &Chain, std::vector<segment_data> const &Pieces);
static void retire(TTrack &Track); static void retire(TTrack &Track);
static void revive(TTrack &Track); static void revive(TTrack &Track);
@@ -139,4 +140,5 @@ class editor_track
static void store_switch_path(TTrack &Switch, int const Path); static void store_switch_path(TTrack &Switch, int const Path);
static void rebuild_geometry(TTrack &Track); static void rebuild_geometry(TTrack &Track);
static TTrack *clone(TTrack const &Template); static TTrack *clone(TTrack const &Template);
static void move_straight(straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End, std::function<bool(TTrack const *)> const &Member);
}; };