diff --git a/application/editormode.h b/application/editormode.h index d8529da0..d099334d 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -289,7 +289,8 @@ class editor_mode : public application_mode, private editor_track::observer void select_track(scene::basic_node *Node); void render_track_gizmo(); void commit_track_drag(bool const Force); - void push_track_snapshot(std::vector> States, std::vector Created = {}); + void push_track_snapshot(std::vector> States, std::vector Created = {}, std::vector Removed = {}); + void trim_history(); void restore_track_snapshot(EditorSnapshot const &Snapshot, std::vector &Opposite, bool const Undo); editor_track::point_ref m_track_point; std::vector m_track_drag; diff --git a/application/editormodeprofile.cpp b/application/editormodeprofile.cpp index 179ce97f..84eef793 100644 --- a/application/editormodeprofile.cpp +++ b/application/editormodeprofile.cpp @@ -869,6 +869,11 @@ void editor_mode::render_profile_canvas() } auto const curves{profile::curves(line)}; + auto const mouse_distance2 = [&](double Chainage, double Elevation) { + auto const dx{x_of(Chainage) - mouse.x}; + auto const dy{y_of(Elevation) - mouse.y}; + return dx * dx + dy * dy; + }; auto const hit_point = [&]() { int hit{-1}; float best{64.0f}; @@ -876,11 +881,9 @@ void editor_mode::render_profile_canvas() { if (points[i].joint) continue; - auto const dx{x_of(points[i].chainage) - mouse.x}; - auto const dy{y_of(points[i].elevation) - mouse.y}; - if (dx * dx + dy * dy < best) + if (auto const distance{mouse_distance2(points[i].chainage, points[i].elevation)}; distance < best) { - best = dx * dx + dy * dy; + best = distance; hit = i; } } @@ -892,9 +895,7 @@ void editor_mode::render_profile_canvas() if (points[c.point].joint) continue; auto const chainage{points[c.point].chainage}; - auto const dx{x_of(chainage) - mouse.x}; - auto const dy{y_of(profile::elevation(line, chainage)) - mouse.y}; - if (dx * dx + dy * dy < 64.0f) + if (mouse_distance2(chainage, profile::elevation(line, chainage)) < 64.0f) return static_cast(c.point); } return -1; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 8d0a2cdf..bfe42d6f 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -108,7 +108,6 @@ bool editor_mode::pick_track_handle() return false; screen_projection const projection; - ImVec2 const mouse = ImGui::GetIO().MousePos; float best = kHandleRadius * kHandleRadius; editor_track::point_ref hit; for (int i = 0; i < static_cast(track->m_paths.size()); ++i) @@ -116,12 +115,7 @@ bool editor_mode::pick_track_handle() for (auto const kind : kPointKinds) { editor_track::point_ref const point{i, kind}; - ImVec2 screen; - if (false == projection.project(editor_track::point_position(*track, point), screen)) - continue; - float const dx = screen.x - mouse.x; - float const dy = screen.y - mouse.y; - float const distance = (dx * dx + dy * dy) * (editor_track::is_end(kind) ? 0.5f : 1.0f); + float const distance = projection.mouse_distance2(editor_track::point_position(*track, point)) * (editor_track::is_end(kind) ? 0.5f : 1.0f); if (distance < best) { best = distance; @@ -208,28 +202,18 @@ void editor_mode::render_track_gizmo() } bool const pointmode = ui()->mode() == nodebank_panel::TRACK && m_track_point.valid() && m_track_point.path < static_cast(track->m_paths.size()); - ImGuizmo::BeginFrame(); - ImGuizmo::SetOrthographic(Global.EditorOrtho); - ImGuiIO const &io = ImGui::GetIO(); - ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); - - glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); - glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); - float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const projection = editor_mode::projection_matrix(aspect); + gizmo_frame const frame; ImGuizmo::OPERATION const operation = (false == pointmode && m_gizmo_op == gizmo_operation::rotate) ? ImGuizmo::ROTATE_Y : ImGuizmo::TRANSLATE; if (false == m_track_gizmo_using) { auto const anchor = pointmode ? editor_track::point_position(*track, m_track_point) : editor_track::pivot(*track); - m_track_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(anchor - camerapos)); + m_track_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(anchor - frame.camera)); } - glm::vec3 snapvalue(operation == ImGuizmo::ROTATE_Y ? 5.0f : m_gizmo_snap); - float const *snap = Global.ctrlState && snapvalue.x > 0.0f ? glm::value_ptr(snapvalue) : nullptr; glm::mat4 delta(1.0f); - ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection), operation, ImGuizmo::WORLD, glm::value_ptr(m_track_gizmo), glm::value_ptr(delta), snap); + frame.manipulate(operation, m_track_gizmo, operation == ImGuizmo::ROTATE_Y ? 5.0f : m_gizmo_snap, &delta); if (ImGuizmo::IsUsing()) { @@ -269,7 +253,7 @@ void editor_mode::render_track_gizmo() } else if (pointmode) { - glm::dvec3 const position = camerapos + glm::dvec3(m_track_gizmo[3]); + glm::dvec3 const position = frame.camera + glm::dvec3(m_track_gizmo[3]); editor_track::move_point(*track, m_track_point, position); for (auto const &connection : m_track_drag_points) editor_track::move_point(*connection.first, connection.second, position); @@ -318,15 +302,19 @@ void editor_mode::commit_track_drag(bool const Force) m_track_last_commit = now; } -void editor_mode::push_track_snapshot(std::vector> States, std::vector Created) +void editor_mode::trim_history() { - if (States.empty() && Created.empty()) - return; - if (m_max_history_size >= 0 && (int)m_history.size() >= m_max_history_size) m_history.erase(m_history.begin(), m_history.begin() + ((int)m_history.size() - m_max_history_size + 1)); +} - auto *track = States.empty() ? Created.front() : States.front().first; +void editor_mode::push_track_snapshot(std::vector> States, std::vector Created, std::vector Removed) +{ + if (States.empty() && Created.empty() && Removed.empty()) + return; + + trim_history(); + auto *track = false == States.empty() ? States.front().first : false == Created.empty() ? Created.front() : Removed.front(); EditorSnapshot snap; snap.action = EditorSnapshot::Action::TrackEdit; snap.node_name = track->name(); @@ -336,6 +324,7 @@ void editor_mode::push_track_snapshot(std::vector(m_route.design.vertices.size()); ++i) { - ImVec2 screen; - if (false == projection.project(route_vertex_position(i), screen)) - continue; - float const dx = screen.x - mouse.x; - float const dy = screen.y - mouse.y; - if (dx * dx + dy * dy < best) + if (auto const distance{projection.mouse_distance2(route_vertex_position(i))}; distance < best) { - best = dx * dx + dy * dy; + best = distance; hit = i; } } int griphit{-1}; for (int i = 0; i < static_cast(m_route.result.vertex_chainages.size()) && m_route.result.length > 0.0; ++i) { - ImVec2 screen; - if (false == projection.project(alignment::evaluate(m_route.result, m_route.result.vertex_chainages[i]).position, screen)) - continue; - float const dx = screen.x - mouse.x; - float const dy = screen.y - mouse.y; - if (dx * dx + dy * dy < best) + if (auto const distance{projection.mouse_distance2(alignment::evaluate(m_route.result, m_route.result.vertex_chainages[i]).position)}; distance < best) { - best = dx * dx + dy * dy; + best = distance; griphit = i; hit = -1; } @@ -1135,21 +1113,12 @@ void editor_mode::render_route_gizmo() return; } - ImGuizmo::BeginFrame(); - ImGuizmo::SetOrthographic(Global.EditorOrtho); - ImGuiIO const &io = ImGui::GetIO(); - ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); - glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); - glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); - float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const projection = editor_mode::projection_matrix(aspect); + gizmo_frame const frame; auto const position{grip ? alignment::evaluate(route.result, route.result.vertex_chainages[route.grip]).position : route_vertex_position(route.vertex)}; if (false == m_route_gizmo_using) - m_route_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(position - camerapos)); - glm::vec3 snapvalue(m_gizmo_snap); - float const *snap = Global.ctrlState && snapvalue.x > 0.0f ? glm::value_ptr(snapvalue) : nullptr; - ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection), ImGuizmo::TRANSLATE, ImGuizmo::WORLD, glm::value_ptr(m_route_gizmo), nullptr, snap); + m_route_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(position - frame.camera)); + frame.manipulate(ImGuizmo::TRANSLATE, m_route_gizmo, m_gizmo_snap); if (false == ImGuizmo::IsUsing()) { @@ -1161,7 +1130,7 @@ void editor_mode::render_route_gizmo() return; } m_route_gizmo_using = true; - glm::dvec3 const moved{camerapos + glm::dvec3(m_route_gizmo[3])}; + glm::dvec3 const moved{frame.camera + glm::dvec3(m_route_gizmo[3])}; if (grip) { auto &vertex{route.design.vertices[route.grip]}; @@ -1812,19 +1781,13 @@ bool editor_mode::pick_straight_handle() auto const &line{current_straight()}; glm::dvec3 const handles[] = {line.start, line.end, (line.start + line.end) * 0.5}; screen_projection const projection; - ImVec2 const mouse = ImGui::GetIO().MousePos; float best = kHandleRadius * kHandleRadius; int hit{-1}; for (int i = 0; i < 3; ++i) { - ImVec2 screen; - if (false == projection.project(handles[i], screen)) - continue; - float const dx = screen.x - mouse.x; - float const dy = screen.y - mouse.y; - if (dx * dx + dy * dy < best) + if (auto const distance{projection.mouse_distance2(handles[i])}; distance < best) { - best = dx * dx + dy * dy; + best = distance; hit = i; } } @@ -1897,22 +1860,13 @@ void editor_mode::render_straight_gizmo() return; } - ImGuizmo::BeginFrame(); - ImGuizmo::SetOrthographic(Global.EditorOrtho); - ImGuiIO const &io = ImGui::GetIO(); - ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); - glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); - glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); - float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const projection = editor_mode::projection_matrix(aspect); + gizmo_frame const frame; glm::dvec3 const anchors[] = {line.start, line.end, (line.start + line.end) * 0.5}; auto const anchor{anchors[state.handle]}; if (false == state.dragging) - state.gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(anchor - camerapos)); - glm::vec3 snapvalue(m_gizmo_snap); - float const *snap = Global.ctrlState && snapvalue.x > 0.0f ? glm::value_ptr(snapvalue) : nullptr; - ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection), ImGuizmo::TRANSLATE, ImGuizmo::WORLD, glm::value_ptr(state.gizmo), nullptr, snap); + state.gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(anchor - frame.camera)); + frame.manipulate(ImGuizmo::TRANSLATE, state.gizmo, m_gizmo_snap); if (ImGuizmo::IsUsing()) { @@ -1944,7 +1898,7 @@ void editor_mode::render_straight_gizmo() } } auto const &grabbed{state.drag_line}; - glm::dvec3 moved{camerapos + glm::dvec3(state.gizmo[3])}; + glm::dvec3 moved{frame.camera + glm::dvec3(state.gizmo[3])}; std::function transform; if (state.handle == 2) { @@ -2250,21 +2204,14 @@ bool editor_mode::place_straight_tool() void editor_mode::render_straight_tool_gizmo() { auto &state{m_straights}; - ImGuizmo::BeginFrame(); - ImGuizmo::SetOrthographic(Global.EditorOrtho); - ImGuiIO const &io = ImGui::GetIO(); - ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); - glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); - glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); - float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; - glm::mat4 const projection = editor_mode::projection_matrix(aspect); + gizmo_frame const frame; if (false == state.tool_dragging) - state.tool_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(state.tool_handle - camerapos)); - ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection), ImGuizmo::TRANSLATE, ImGuizmo::WORLD, glm::value_ptr(state.tool_gizmo), nullptr, nullptr); + state.tool_gizmo = glm::translate(glm::mat4(1.0f), glm::vec3(state.tool_handle - frame.camera)); + frame.manipulate(ImGuizmo::TRANSLATE, state.tool_gizmo, 0.0f); if (ImGuizmo::IsUsing()) { state.tool_dragging = true; - auto const moved{camerapos + glm::dvec3(state.tool_gizmo[3])}; + auto const moved{frame.camera + glm::dvec3(state.tool_gizmo[3])}; state.tool_handle = {moved.x, state.tool_handle.y, moved.z}; } else if (state.tool_dragging) @@ -2365,8 +2312,7 @@ void editor_mode::delete_selected_track() snap.node_name = track->name(); snap.position = track->location(); snap.removed = {track}; - if (m_max_history_size >= 0 && (int)m_history.size() >= m_max_history_size) - m_history.erase(m_history.begin(), m_history.begin() + ((int)m_history.size() - m_max_history_size + 1)); + trim_history(); m_history.push_back(std::move(snap)); g_redo.clear(); editor_track::retire(*track); @@ -2422,7 +2368,6 @@ bool editor_mode::start_extend() if (track == nullptr || (track->eType != tt_Normal && track->eType != tt_Switch) || false == editor_track::is_supported(*track)) return false; screen_projection const projection; - ImVec2 const mouse = ImGui::GetIO().MousePos; for (int i = 0; i < static_cast(track->m_paths.size()); ++i) { auto const &path{track->m_paths[i]}; @@ -2432,10 +2377,7 @@ bool editor_mode::start_extend() if (editor_track::is_connected(*track, point)) continue; auto const position{editor_track::point_position(*track, point)}; - ImVec2 screen; - if (false == projection.project(position, screen)) - continue; - if ((screen.x - mouse.x) * (screen.x - mouse.x) + (screen.y - mouse.y) * (screen.y - mouse.y) > kHandleRadius * kHandleRadius * 1.5f) + if (projection.mouse_distance2(position) > kHandleRadius * kHandleRadius * 1.5f) continue; auto const &start{path.points[segment_data::point::start]}; auto const &end{path.points[segment_data::point::end]}; @@ -2576,8 +2518,7 @@ void editor_mode::finish_extend() editor_track::commit({insert}); created.push_back(insert); } - push_track_snapshot(std::move(states), std::move(created)); - m_history.back().removed = std::move(removed); + push_track_snapshot(std::move(states), std::move(created), std::move(removed)); straight_refresh(); return; } @@ -2692,8 +2633,7 @@ void editor_mode::insert_switch(editor_track::straight const &Line, double const std::vector removed; if (false == place_switch_on_straight(Line, m_switch.templates[m_switch.armed], nullptr, Along, Direction, Side, true, states, created, removed)) return; - push_track_snapshot(std::move(states), std::move(created)); - m_history.back().removed = std::move(removed); + push_track_snapshot(std::move(states), std::move(created), std::move(removed)); straight_refresh(); } @@ -3408,8 +3348,7 @@ bool editor_mode::insert_double_slip(editor_track::straight const &Line, glm::dv auto const parts{build_double_slip(pointA, pointB, pointC, pointD, crossing, heightfirst(alongfirst), first, direction, angle, radius, style != nullptr ? *style : *Line.tracks.front())}; editor_track::commit(parts); created.insert(created.end(), parts.begin(), parts.end()); - push_track_snapshot(std::move(states), std::move(created)); - m_history.back().removed = std::move(removed); + push_track_snapshot(std::move(states), std::move(created), std::move(removed)); straight_refresh(); return true; } @@ -3548,8 +3487,7 @@ bool editor_mode::replace_double_slip(TTrack &Part, editor_track::switch_templat commitlist.push_back(track); editor_track::commit(commitlist); created.insert(created.end(), parts2.begin(), parts2.end()); - push_track_snapshot(std::move(states), std::move(created)); - m_history.back().removed = std::move(removed); + push_track_snapshot(std::move(states), std::move(created), std::move(removed)); straight_refresh(); return true; } @@ -3729,7 +3667,6 @@ void editor_mode::insert_curved_switch(int const Direction, int const Side) editor_track::commit({track}); created.push_back(track); } - push_track_snapshot(std::move(states), std::move(created)); - m_history.back().removed = std::move(removed); + push_track_snapshot(std::move(states), std::move(created), std::move(removed)); straight_refresh(); } diff --git a/application/editorprojection.h b/application/editorprojection.h index aa5cd45d..f867b62d 100644 --- a/application/editorprojection.h +++ b/application/editorprojection.h @@ -11,7 +11,11 @@ http://mozilla.org/MPL/2.0/. #include "application/editormode.h" #include "rendering/renderer.h" +#include "utilities/Globals.h" #include "imgui/imgui.h" +#include "imgui/ImGuizmo.h" + +#include // world to screen projection of the current camera, for overlays drawn with ImGui class screen_projection @@ -54,6 +58,15 @@ class screen_projection b = glm::mix(b, a, (kNear - b.w) / (a.w - b.w)); Drawlist->AddLine(screen(a), screen(b), Color, Thickness); } + // squared distance on the screen from the mouse cursor to the point, the largest float for points behind the camera + float mouse_distance2(glm::dvec3 const &Point) const + { + ImVec2 position; + if (false == project(Point, position)) + return std::numeric_limits::max(); + auto const &mouse{ImGui::GetIO().MousePos}; + return (position.x - mouse.x) * (position.x - mouse.x) + (position.y - mouse.y) * (position.y - mouse.y); + } private: static constexpr float kNear{0.1f}; @@ -61,3 +74,29 @@ class screen_projection glm::dvec3 m_camera; glm::mat4 m_viewprojection; }; + +// starts a gizmo frame over the whole display, the view and the projection are those of the current camera +struct gizmo_frame +{ + glm::mat4 view; + glm::mat4 projection; + glm::dvec3 camera; + + gizmo_frame() + { + ImGuizmo::BeginFrame(); + ImGuizmo::SetOrthographic(Global.EditorOrtho); + ImGuiIO const &io = ImGui::GetIO(); + ImGuizmo::SetRect(0.0f, 0.0f, io.DisplaySize.x, io.DisplaySize.y); + view = GfxRenderer->Camera_View_Matrix(); + camera = GfxRenderer->Camera_Position(); + projection = editor_mode::projection_matrix(io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f); + } + // moves the gizmo, placed at the point given in world space, with snapping to the step when ctrl is held + bool manipulate(ImGuizmo::OPERATION const Operation, glm::mat4 &Gizmo, float const Snap, glm::mat4 *Delta = nullptr) const + { + glm::vec3 snapvalue(Snap); + float const *snap = Global.ctrlState && Snap > 0.0f ? &snapvalue.x : nullptr; + return ImGuizmo::Manipulate(&view[0][0], &projection[0][0], Operation, ImGuizmo::WORLD, &Gizmo[0][0], Delta != nullptr ? &(*Delta)[0][0] : nullptr, snap); + } +};