mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 14:29:20 +02:00
730 lines
27 KiB
C++
730 lines
27 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "application/editormode.h"
|
|
#include "application/editoruilayer.h"
|
|
#include "application/editorprojection.h"
|
|
#include "editor/editorGeometry.hpp"
|
|
|
|
#include "utilities/Globals.h"
|
|
#include "rendering/renderer.h"
|
|
#include "world/Track.h"
|
|
|
|
#include "imgui/imgui.h"
|
|
#include "imgui/ImGuizmo.h"
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
|
|
namespace
|
|
{
|
|
|
|
using geometry::bezier;
|
|
using geometry::level;
|
|
using geometry::plan_of;
|
|
using geometry::turned;
|
|
|
|
bool mouse_along(glm::dvec3 const &Origin, glm::dvec3 const &Axis, double &Along)
|
|
{
|
|
screen_projection const projection;
|
|
glm::dvec3 origin;
|
|
glm::dvec3 direction;
|
|
projection.ray(ImGui::GetIO().MousePos, origin, direction);
|
|
auto const b{glm::dot(Axis, direction)};
|
|
auto const denominator{1.0 - b * b};
|
|
if (denominator < 1e-6)
|
|
return false;
|
|
auto const w{Origin - origin};
|
|
Along = (b * glm::dot(direction, w) - glm::dot(Axis, w)) / denominator;
|
|
return true;
|
|
}
|
|
|
|
glm::mat4 gizmo_along(glm::dvec3 const &Position, glm::dvec3 const &Axis)
|
|
{
|
|
auto gizmo{glm::translate(glm::mat4(1.0f), glm::vec3(Position))};
|
|
if (glm::length(Axis) < 1e-9)
|
|
return gizmo;
|
|
auto const z{glm::normalize(Axis)};
|
|
auto x{glm::cross(glm::dvec3{0.0, 1.0, 0.0}, z)};
|
|
x = glm::length(x) > 1e-6 ? glm::normalize(x) : glm::dvec3{1.0, 0.0, 0.0};
|
|
auto const y{glm::cross(z, x)};
|
|
gizmo[0] = glm::vec4(glm::vec3(x), 0.0f);
|
|
gizmo[1] = glm::vec4(glm::vec3(y), 0.0f);
|
|
gizmo[2] = glm::vec4(glm::vec3(z), 0.0f);
|
|
return gizmo;
|
|
}
|
|
|
|
void add_once(std::vector<TTrack *> &Tracks, TTrack *Track)
|
|
{
|
|
if (std::find(Tracks.begin(), Tracks.end(), Track) == Tracks.end())
|
|
Tracks.emplace_back(Track);
|
|
}
|
|
|
|
bool editable(TTrack const &Track)
|
|
{
|
|
std::string reason;
|
|
return editor_track::can_edit_geometry(Track, reason);
|
|
}
|
|
|
|
double snapped_grade(double const Grade)
|
|
{
|
|
return std::round(Grade * 1000.0) / 1000.0;
|
|
}
|
|
|
|
double snapped_cant(double const Roll0, double const Roll)
|
|
{
|
|
auto const cant{std::round(1500.0 * std::sin(glm::radians(Roll0 + Roll)) / 5.0) * 5.0 / 1500.0};
|
|
return glm::degrees(std::asin(std::clamp(cant, -1.0, 1.0))) - Roll0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
editor_mode::gizmo_operation editor_mode::track_gizmo_operation(TTrack const &Track, bool const Pointmode) const
|
|
{
|
|
if (m_gizmo_op == gizmo_operation::translate || (Pointmode && false == editor_track::is_end(m_track_point.kind)))
|
|
return m_gizmo_op;
|
|
if (m_gizmo_op == gizmo_operation::rotate)
|
|
return gizmo_operation::rotate;
|
|
return editor_track::stretch_end(Track, Pointmode ? m_track_point : editor_track::point_ref{}).has_value() ? gizmo_operation::scale : gizmo_operation::translate;
|
|
}
|
|
|
|
glm::mat4 editor_mode::track_gizmo_frame(TTrack const &Track, bool const Pointmode, gizmo_operation const Operation) const
|
|
{
|
|
auto anchor{Pointmode ? editor_track::point_position(Track, m_track_point) : editor_track::pivot(Track)};
|
|
bool const controlmode{Pointmode && false == editor_track::is_end(m_track_point.kind)};
|
|
glm::dvec3 axis{0.0};
|
|
if (Operation == gizmo_operation::scale)
|
|
{
|
|
if (controlmode)
|
|
axis = editor_track::control_vector(Track, m_track_point);
|
|
else if (auto const end{editor_track::stretch_end(Track, Pointmode ? m_track_point : editor_track::point_ref{})})
|
|
std::tie(anchor, axis) = editor_track::end_axis(Track.m_paths[end->path], end->atend);
|
|
}
|
|
else if (Operation == gizmo_operation::rotate)
|
|
{
|
|
if (controlmode)
|
|
axis = level(editor_track::control_vector(Track, m_track_point));
|
|
else if (Pointmode)
|
|
axis = level(bezier{Track.m_paths[m_track_point.path]}.first(m_track_point.kind == editor_track::point_kind::start ? 0.0 : 1.0));
|
|
else
|
|
{
|
|
auto const middle{editor_track::middle(Track)};
|
|
anchor = middle.point;
|
|
axis = level(middle.tangent);
|
|
}
|
|
}
|
|
return gizmo_along(anchor - glm::dvec3{GfxRenderer->Camera_Position()}, axis);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_hold()
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
drag.held.clear();
|
|
for (auto *track : m_track_drag)
|
|
{
|
|
if (drag.operation == gizmo_operation::scale && false == drag.control && track == selected_track())
|
|
continue;
|
|
auto held{editor_track::hold_controls(*track)};
|
|
if (false == held.empty())
|
|
drag.held.emplace_back(track, std::move(held));
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_settle()
|
|
{
|
|
for (auto &[track, held] : m_track_gizmo_drag.held)
|
|
editor_track::settle_controls(*track, held);
|
|
m_track_gizmo_drag.held.clear();
|
|
}
|
|
|
|
void editor_mode::track_gizmo_conform(TTrack &Track) const
|
|
{
|
|
if (Track.eType != tt_Switch)
|
|
return;
|
|
editor_track::conform_switch(Track);
|
|
editor_track::follow(Track, m_track_joints);
|
|
editor_track::follow_grade(Track, m_track_joints);
|
|
}
|
|
|
|
bool editor_mode::track_gizmo_clustered(TTrack const *Track) const
|
|
{
|
|
return std::any_of(m_track_gizmo_drag.switches.begin(), m_track_gizmo_drag.switches.end(), [&](auto const &Link) { return std::any_of(Link.cluster.begin(), Link.cluster.end(), [&](auto const &Member) { return Member.first == Track; }); });
|
|
}
|
|
|
|
void editor_mode::track_gizmo_stretch(TTrack &Track, double const Length)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto const pieces{editor_track::stretched(drag.paths[drag.path], drag.atend, Length)};
|
|
if (pieces.empty())
|
|
return;
|
|
auto paths{drag.paths};
|
|
paths.erase(paths.begin() + drag.path);
|
|
paths.insert(paths.begin() + drag.path, pieces.begin(), pieces.end());
|
|
Track.m_paths = std::move(paths);
|
|
drag.pieces = pieces.size();
|
|
drag.stretch = Length;
|
|
if (drag.point.valid())
|
|
m_track_point = editor_track::stretch_remap(drag.point, drag.path, drag.pieces);
|
|
auto joints{m_track_joints};
|
|
for (auto &joint : joints)
|
|
joint.own = editor_track::stretch_remap(joint.own, drag.path, drag.pieces);
|
|
editor_track::follow(Track, joints);
|
|
track_gizmo_spill(Track, pieces.size());
|
|
m_track_dirty = true;
|
|
}
|
|
|
|
void editor_mode::track_gizmo_spill(TTrack &Track, std::size_t const Pieces)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
std::vector<segment_data> rest;
|
|
if (Track.eType == tt_Normal && Pieces > 1)
|
|
{
|
|
auto const first{Track.m_paths.begin() + drag.path};
|
|
auto const keep{drag.atend ? first : first + (Pieces - 1)};
|
|
for (auto piece = first; piece != first + Pieces; ++piece)
|
|
if (piece != keep)
|
|
rest.push_back(*piece);
|
|
auto const kept{*keep};
|
|
Track.m_paths.erase(first, first + Pieces);
|
|
Track.m_paths.insert(Track.m_paths.begin() + drag.path, kept);
|
|
if (drag.point.valid())
|
|
m_track_point = drag.point;
|
|
}
|
|
while (drag.extra.size() > rest.size())
|
|
{
|
|
auto *surplus{drag.extra.back()};
|
|
drag.extra.pop_back();
|
|
std::erase(m_track_drag, surplus);
|
|
editor_track::retire(*surplus);
|
|
}
|
|
for (std::size_t i = 0; i < rest.size(); ++i)
|
|
{
|
|
if (i < drag.extra.size())
|
|
{
|
|
drag.extra[i]->m_paths = {rest[i]};
|
|
continue;
|
|
}
|
|
auto *created{editor_track::create_path(Track, rest[i])};
|
|
drag.extra.push_back(created);
|
|
m_track_drag.push_back(created);
|
|
editor_track::commit({created});
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_control(TTrack &Track, glm::dvec3 Control)
|
|
{
|
|
auto const &drag{m_track_gizmo_drag};
|
|
if (drag.fixed != glm::dvec3{})
|
|
Control = drag.fixed * std::max(0.5, glm::dot(Control, drag.fixed));
|
|
if (glm::length(Control) < 0.5)
|
|
Control = glm::length(Control) > 1e-9 ? glm::normalize(Control) * 0.5 : drag.axis * 0.5;
|
|
editor_track::move_point(Track, drag.point, editor_track::point_position(Track, editor_track::end_of(drag.point)) + Control);
|
|
editor_track::align_controls(Track, drag.point, drag.partners);
|
|
track_gizmo_conform(Track);
|
|
m_track_dirty = true;
|
|
}
|
|
|
|
void editor_mode::track_gizmo_point(TTrack &Track, glm::dvec3 const &Position)
|
|
{
|
|
editor_track::move_point(Track, m_track_point, Position);
|
|
track_gizmo_conform(Track);
|
|
auto const moved{editor_track::point_position(Track, m_track_point)};
|
|
for (auto const &[track, point] : m_track_drag_points)
|
|
if (false == track_gizmo_clustered(track))
|
|
editor_track::move_point(*track, point, moved);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_carry(track_gizmo_drag::switch_joint &Link, glm::dvec3 const &Point, glm::dvec3 const &From, glm::dvec3 const &To, double const Grade)
|
|
{
|
|
auto const c{glm::dot(From, To)};
|
|
auto const s{From.x * To.z - From.z * To.x};
|
|
auto const turn = [&](glm::dvec3 const &Vector) { return glm::dvec3{Vector.x * c - Vector.z * s, Vector.y, Vector.z * c + Vector.x * s}; };
|
|
auto const tilt = [&](glm::dvec3 const &Vector) { return (Vector.x * To.x + Vector.z * To.z) * Grade; };
|
|
for (auto &[member, paths] : Link.cluster)
|
|
{
|
|
member->m_paths = paths;
|
|
for (auto &path : member->m_paths)
|
|
{
|
|
for (auto const index : {segment_data::point::start, segment_data::point::end})
|
|
{
|
|
auto const moved{turn(path.points[index] - Link.point)};
|
|
path.points[index] = Point + moved + glm::dvec3{0.0, tilt(moved), 0.0};
|
|
}
|
|
for (auto const index : {segment_data::point::control1, segment_data::point::control2})
|
|
{
|
|
auto &control{path.points[index]};
|
|
if (control == glm::dvec3{})
|
|
continue;
|
|
control = turn(control);
|
|
control.y += tilt(control);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_roll_switch(track_gizmo_drag::switch_joint const &Link, double const Roll)
|
|
{
|
|
auto &main{Link.track->m_paths[0]};
|
|
bool const start{Link.theirs.kind == editor_track::point_kind::start};
|
|
auto value{start ? -Roll : Roll};
|
|
if (Link.theirs.path != 0)
|
|
{
|
|
auto const t{start ? 0.0 : 1.0};
|
|
if (glm::dot(plan_of(bezier{Link.track->m_paths[Link.theirs.path]}.first(t)), plan_of(bezier{main}.first(t))) < 0.0)
|
|
value = -value;
|
|
}
|
|
main.rolls = {static_cast<float>(value), static_cast<float>(value)};
|
|
editor_track::conform_switch(*Link.track);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_switches(TTrack &Track)
|
|
{
|
|
for (auto &link : m_track_gizmo_drag.switches)
|
|
{
|
|
if (link.own.path >= static_cast<int>(Track.m_paths.size()))
|
|
continue;
|
|
auto const point{editor_track::point_position(Track, link.own)};
|
|
auto const tangent{editor_track::outward(Track, link.own)};
|
|
auto const roll{editor_track::joint_roll(Track, link.own)};
|
|
auto const from{level(link.tangent)};
|
|
auto const to{level(tangent)};
|
|
auto const grade{geometry::grade_of(tangent) - geometry::grade_of(link.tangent)};
|
|
bool const changed{glm::distance(point, link.point) > 1e-9 || glm::distance(from, to) > 1e-12 || std::abs(grade) > 1e-12 || std::abs(roll - link.roll) > 1e-6};
|
|
if (false == changed && false == link.moved)
|
|
continue;
|
|
link.moved = true;
|
|
track_gizmo_carry(link, point, from, to, grade);
|
|
if (std::abs(roll - link.roll) > 1e-6)
|
|
track_gizmo_roll_switch(link, roll);
|
|
for (auto const &[member, joints] : link.joints)
|
|
{
|
|
editor_track::follow(*member, joints);
|
|
editor_track::follow_grade(*member, joints);
|
|
editor_track::match_rolls(*member, joints);
|
|
}
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_take_joints(TTrack &Track)
|
|
{
|
|
m_track_joints = editor_track::joints(Track);
|
|
for (auto const &joint : m_track_joints)
|
|
add_once(m_track_drag, joint.other);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_take_points(TTrack &Track)
|
|
{
|
|
for (auto const &connection : editor_track::connected_points(Track, editor_track::point_position(Track, m_track_point)))
|
|
{
|
|
if (false == editable(*connection.first))
|
|
continue;
|
|
m_track_drag_points.emplace_back(connection);
|
|
add_once(m_track_drag, connection.first);
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_take_partners(TTrack &Track)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto const end{editor_track::end_of(m_track_point)};
|
|
for (auto const &partner : editor_track::partners(Track, end))
|
|
{
|
|
auto const &[other, theirs]{partner};
|
|
bool const own{other == &Track};
|
|
bool const fixed{own ? m_track_point.path != 0 : other->eType != tt_Normal || false == editable(*other)};
|
|
if (fixed)
|
|
{
|
|
auto const &path{other->m_paths[theirs.path]};
|
|
bool const start{theirs.kind == editor_track::point_kind::start};
|
|
auto const control{path.points[start ? segment_data::point::control1 : segment_data::point::control2]};
|
|
auto const chord{path.points[start ? segment_data::point::end : segment_data::point::start] - path.points[start ? segment_data::point::start : segment_data::point::end]};
|
|
auto const along{control != glm::dvec3{} ? control : chord};
|
|
if (glm::length(along) > 1e-9)
|
|
drag.fixed = (own && theirs.kind == end.kind ? 1.0 : -1.0) * glm::normalize(along);
|
|
continue;
|
|
}
|
|
if (false == m_track_drag_connected && false == own)
|
|
continue;
|
|
drag.partners.push_back(partner);
|
|
add_once(m_track_drag, other);
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_take_switch_joints(TTrack &Track, bool const Controlmode)
|
|
{
|
|
auto const edited{editor_track::point_position(Track, editor_track::end_of(m_track_point))};
|
|
for (auto const &joint : editor_track::joints(Track))
|
|
{
|
|
if (Controlmode && glm::distance(editor_track::point_position(Track, joint.own), edited) < geometry::same_point)
|
|
continue;
|
|
m_track_joints.push_back(joint);
|
|
add_once(m_track_drag, joint.other);
|
|
}
|
|
}
|
|
|
|
editor_mode::track_gizmo_drag::switch_joint editor_mode::track_gizmo_link(TTrack &Track, editor_track::point_ref const &Own, std::pair<TTrack *, editor_track::point_ref> const &Connection, std::function<bool(TTrack const *)> const &Movable)
|
|
{
|
|
track_gizmo_drag::switch_joint link{Connection.first, Own, Connection.second, editor_track::point_position(Track, Own), editor_track::outward(Track, Own), editor_track::joint_roll(Track, Own)};
|
|
std::vector<TTrack *> members{Connection.first};
|
|
for (std::size_t k = 0; k < members.size(); ++k)
|
|
for (auto *next : editor_track::neighbours(*members[k]))
|
|
if (Movable(next) && std::find(members.begin(), members.end(), next) == members.end())
|
|
members.push_back(next);
|
|
for (auto *member : members)
|
|
{
|
|
link.cluster.emplace_back(member, std::vector<segment_data>{});
|
|
auto joints{editor_track::joints(*member)};
|
|
std::erase_if(joints, [&](editor_track::joint const &Joint) { return Joint.other == &Track; });
|
|
add_once(m_track_drag, member);
|
|
for (auto const &joint : joints)
|
|
add_once(m_track_drag, joint.other);
|
|
link.joints.emplace_back(member, std::move(joints));
|
|
}
|
|
return link;
|
|
}
|
|
|
|
void editor_mode::track_gizmo_take_switches(TTrack &Track)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto const movable = [&](TTrack const *Other) { return Other != &Track && Other->eType == tt_Switch && Other->m_paths.size() == 2 && editable(*Other) && false == track_gizmo_clustered(Other); };
|
|
for (int i = 0; i < static_cast<int>(Track.m_paths.size()); ++i)
|
|
for (auto const kind : {editor_track::point_kind::start, editor_track::point_kind::end})
|
|
{
|
|
editor_track::point_ref const own{i, kind};
|
|
for (auto const &connection : editor_track::connected_points(Track, editor_track::point_position(Track, own)))
|
|
if (movable(connection.first))
|
|
drag.switches.push_back(track_gizmo_link(Track, own, connection, movable));
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_measure_rotation(TTrack &Track, bool const Pointmode)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto const frame{track_gizmo_frame(Track, Pointmode, gizmo_operation::rotate)};
|
|
drag.basis = glm::dmat3(glm::mat3(frame));
|
|
drag.pivot = glm::dvec3{GfxRenderer->Camera_Position()} + glm::dvec3(frame[3]);
|
|
double along{0.0};
|
|
for (auto const &path : Track.m_paths)
|
|
{
|
|
drag.chainages.push_back(along);
|
|
drag.lengths.push_back(bezier{path}.plan_length());
|
|
along += drag.lengths.back();
|
|
}
|
|
auto const end{editor_track::switch_main(Track, editor_track::end_of(drag.point))};
|
|
if (Track.eType == tt_Switch && false == Track.m_paths.empty())
|
|
{
|
|
drag.pivot_chainage = editor_track::switch_station(Track, drag.pivot);
|
|
drag.roll0 = end.valid() && end.kind == editor_track::point_kind::end ? Track.m_paths[0].rolls[1] : Track.m_paths[0].rolls[0];
|
|
}
|
|
else if (end.valid() && end.path < static_cast<int>(drag.chainages.size()))
|
|
{
|
|
drag.pivot_chainage = drag.chainages[end.path] + (end.kind == editor_track::point_kind::end ? drag.lengths[end.path] : 0.0);
|
|
drag.roll0 = end.kind == editor_track::point_kind::start ? Track.m_paths[end.path].rolls[0] : Track.m_paths[end.path].rolls[1];
|
|
}
|
|
else
|
|
{
|
|
drag.pivot_chainage = along * 0.5;
|
|
drag.roll0 = Track.m_paths.empty() ? 0.0 : Track.m_paths.front().rolls[0];
|
|
}
|
|
}
|
|
|
|
void editor_mode::track_gizmo_measure(TTrack &Track, bool const Pointmode, gizmo_operation const Operation)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
if (drag.control)
|
|
{
|
|
drag.origin = editor_track::point_position(Track, m_track_point);
|
|
auto const control{editor_track::control_vector(Track, m_track_point)};
|
|
drag.length = glm::length(control);
|
|
drag.axis = drag.length > 1e-9 ? control / drag.length : glm::dvec3{0.0, 0.0, 1.0};
|
|
}
|
|
else if (auto const end{Operation == gizmo_operation::scale ? editor_track::stretch_end(Track, drag.point) : std::nullopt})
|
|
{
|
|
drag.path = end->path;
|
|
drag.atend = end->atend;
|
|
drag.paths = Track.m_paths;
|
|
std::tie(drag.origin, drag.axis) = editor_track::end_axis(Track.m_paths[drag.path], drag.atend);
|
|
drag.length = bezier{Track.m_paths[drag.path]}.plan_length();
|
|
}
|
|
drag.stretch = drag.length;
|
|
if (Operation == gizmo_operation::rotate)
|
|
track_gizmo_measure_rotation(Track, Pointmode);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_begin(TTrack &Track, bool const Pointmode, gizmo_operation const Operation)
|
|
{
|
|
bool const controlmode{Pointmode && false == editor_track::is_end(m_track_point.kind)};
|
|
auto &drag{m_track_gizmo_drag};
|
|
m_track_gizmo_using = true;
|
|
m_track_pivot = editor_track::pivot(Track);
|
|
m_track_drag = {&Track};
|
|
m_track_drag_points.clear();
|
|
m_track_joints.clear();
|
|
m_track_snap = {};
|
|
drag = {};
|
|
drag.operation = Operation;
|
|
drag.control = controlmode;
|
|
drag.point = Pointmode ? m_track_point : editor_track::point_ref{};
|
|
bool const whole{false == Pointmode || (false == controlmode && Operation != gizmo_operation::translate)};
|
|
if (whole && m_track_drag_connected)
|
|
track_gizmo_take_joints(Track);
|
|
if (Pointmode && false == controlmode && Operation == gizmo_operation::translate && m_track_drag_connected)
|
|
track_gizmo_take_points(Track);
|
|
if (controlmode)
|
|
track_gizmo_take_partners(Track);
|
|
if (false == whole && Track.eType == tt_Switch && m_track_drag_connected)
|
|
track_gizmo_take_switch_joints(Track, controlmode);
|
|
if (m_track_drag_connected)
|
|
track_gizmo_take_switches(Track);
|
|
std::vector<std::pair<TTrack *, editor_track::state>> states;
|
|
for (auto *dragged : m_track_drag)
|
|
states.emplace_back(dragged, editor_track::capture(*dragged));
|
|
push_track_snapshot(std::move(states));
|
|
records_watch_begin();
|
|
track_gizmo_hold();
|
|
for (auto &link : drag.switches)
|
|
for (auto &[member, paths] : link.cluster)
|
|
paths = member->m_paths;
|
|
track_gizmo_measure(Track, Pointmode, Operation);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_turn_direction(TTrack &Track, double const Angle)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto target{Angle};
|
|
if (Global.ctrlState)
|
|
target = glm::radians(std::round(glm::degrees(target) / 5.0) * 5.0);
|
|
auto const step{std::remainder(target - drag.angle, 2.0 * glm::pi<double>())};
|
|
if (std::abs(step) < 1e-9)
|
|
return;
|
|
drag.angle += step;
|
|
if (drag.control)
|
|
{
|
|
auto const control{editor_track::control_vector(Track, drag.point)};
|
|
auto const plan{turned(plan_of(control), -step)};
|
|
track_gizmo_control(Track, {plan.x, control.y, plan.y});
|
|
return;
|
|
}
|
|
editor_track::rotate_y(Track, drag.pivot, step);
|
|
editor_track::follow(Track, m_track_joints);
|
|
m_track_dirty = true;
|
|
}
|
|
|
|
void editor_mode::track_gizmo_turn_grade(TTrack &Track, double const Angle)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto target{-std::tan(std::clamp(Angle, -1.4, 1.4))};
|
|
if (Global.ctrlState)
|
|
target = snapped_grade(target);
|
|
auto const step{target - drag.grade};
|
|
if (std::abs(step) < 1e-9)
|
|
return;
|
|
drag.grade = target;
|
|
if (drag.control)
|
|
{
|
|
auto const control{editor_track::control_vector(Track, drag.point)};
|
|
auto const run{glm::length(plan_of(control))};
|
|
auto const along{glm::dot(level(control), drag.basis[2])};
|
|
track_gizmo_control(Track, {control.x, control.y + step * run * (along >= 0.0 ? 1.0 : -1.0), control.z});
|
|
return;
|
|
}
|
|
auto const count{editor_track::driven_paths(Track)};
|
|
for (std::size_t i = 0; i < count && i < drag.chainages.size(); ++i)
|
|
{
|
|
auto &path{Track.m_paths[i]};
|
|
auto const from{drag.chainages[i] - drag.pivot_chainage};
|
|
auto const to{from + drag.lengths[i]};
|
|
path.points[segment_data::point::start].y += step * from;
|
|
path.points[segment_data::point::end].y += step * to;
|
|
auto &control1{path.points[segment_data::point::control1]};
|
|
auto &control2{path.points[segment_data::point::control2]};
|
|
control1.y += step * glm::length(plan_of(control1));
|
|
control2.y -= step * glm::length(plan_of(control2));
|
|
}
|
|
editor_track::conform_switch(Track);
|
|
editor_track::follow(Track, m_track_joints);
|
|
editor_track::follow_grade(Track, m_track_joints);
|
|
m_track_dirty = true;
|
|
}
|
|
|
|
void editor_mode::track_gizmo_turn_cant(TTrack &Track, double const Angle)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto const end{editor_track::end_of(drag.point)};
|
|
auto const canted{editor_track::switch_main(Track, end)};
|
|
auto target{glm::degrees(drag.point.kind == editor_track::point_kind::control2 ? -Angle : Angle)};
|
|
if (Global.ctrlState)
|
|
target = snapped_cant(drag.roll0, target);
|
|
auto const step{static_cast<float>(target - drag.roll)};
|
|
if (std::abs(step) < 1e-6f)
|
|
return;
|
|
drag.roll = target;
|
|
for (int i = 0; i < static_cast<int>(Track.m_paths.size()); ++i)
|
|
{
|
|
auto &path{Track.m_paths[i]};
|
|
if (false == canted.valid() || (i == canted.path && canted.kind == editor_track::point_kind::start))
|
|
path.rolls[0] += step;
|
|
if (false == canted.valid() || (i == canted.path && canted.kind == editor_track::point_kind::end))
|
|
path.rolls[1] += step;
|
|
}
|
|
track_gizmo_conform(Track);
|
|
editor_track::match_rolls(Track, m_track_joints);
|
|
for (auto const &[other, theirs] : drag.partners)
|
|
if (other != &Track)
|
|
editor_track::roll_joined(*other, theirs, static_cast<float>(editor_track::joint_roll(Track, end)));
|
|
m_track_dirty = true;
|
|
}
|
|
|
|
void editor_mode::track_gizmo_turn(TTrack &Track, int const Axis, double const Angle)
|
|
{
|
|
auto const &drag{m_track_gizmo_drag};
|
|
if (drag.control && drag.fixed != glm::dvec3{})
|
|
return;
|
|
if (Axis == 1)
|
|
track_gizmo_turn_direction(Track, Angle);
|
|
else if (Axis == 0)
|
|
track_gizmo_turn_grade(Track, Angle);
|
|
else
|
|
track_gizmo_turn_cant(Track, Angle);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_length(TTrack &Track, double Length)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
Length = std::max(drag.control ? 0.5 : 1.0, Length);
|
|
if (std::abs(Length - drag.stretch) < 1e-6)
|
|
return;
|
|
if (drag.control)
|
|
{
|
|
drag.stretch = Length;
|
|
track_gizmo_control(Track, drag.axis * Length);
|
|
}
|
|
else if (drag.path >= 0)
|
|
track_gizmo_stretch(Track, Length);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_end(TTrack &Track)
|
|
{
|
|
if (m_track_snap.track != nullptr && m_track_gizmo_drag.operation == gizmo_operation::translate && m_track_point.valid())
|
|
editor_track::snap_point(Track, m_track_point, m_track_snap, m_track_align_tangent);
|
|
track_gizmo_settle();
|
|
m_track_dirty = true;
|
|
m_track_snap = {};
|
|
commit_track_drag(true);
|
|
if (false == m_track_gizmo_drag.extra.empty() && false == m_history.empty())
|
|
{
|
|
auto &created{m_history.back().created};
|
|
created.insert(created.end(), m_track_gizmo_drag.extra.begin(), m_track_gizmo_drag.extra.end());
|
|
m_track_gizmo_drag.extra.clear();
|
|
}
|
|
records_watch_end();
|
|
m_track_gizmo_using = false;
|
|
m_track_drag.clear();
|
|
m_track_drag_points.clear();
|
|
m_track_joints.clear();
|
|
}
|
|
|
|
void editor_mode::track_gizmo_rotated(TTrack &Track)
|
|
{
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto const turn{glm::transpose(drag.basis) * glm::dmat3(glm::mat3(m_track_gizmo))};
|
|
std::array<double, 3> const angles{std::atan2(turn[1][2], turn[1][1]), std::atan2(turn[2][0], turn[0][0]), std::atan2(turn[0][1], turn[0][0])};
|
|
if (drag.ring < 0)
|
|
{
|
|
auto const largest{std::max_element(angles.begin(), angles.end(), [](double const A, double const B) { return std::abs(A) < std::abs(B); })};
|
|
if (std::abs(*largest) > 1e-5)
|
|
drag.ring = static_cast<int>(largest - angles.begin());
|
|
}
|
|
if (drag.ring >= 0)
|
|
track_gizmo_turn(Track, drag.ring, angles[drag.ring]);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_scaled(TTrack &Track)
|
|
{
|
|
auto const &drag{m_track_gizmo_drag};
|
|
double along{0.0};
|
|
if (false == mouse_along(drag.origin, drag.axis, along))
|
|
return;
|
|
auto length{drag.length + along - drag.grabbed};
|
|
if (Global.ctrlState && m_gizmo_snap > 0.0f)
|
|
length = std::round(length / m_gizmo_snap) * m_gizmo_snap;
|
|
track_gizmo_length(Track, length);
|
|
}
|
|
|
|
void editor_mode::track_gizmo_moved_point(TTrack &Track, glm::dvec3 const &Position)
|
|
{
|
|
track_gizmo_point(Track, Position);
|
|
m_track_dirty = true;
|
|
if (false == editor_track::is_end(m_track_point.kind) || false == m_track_drag_points.empty())
|
|
return;
|
|
std::vector<TTrack const *> const exclude(m_track_drag.begin(), m_track_drag.end());
|
|
screen_projection const projection;
|
|
ImVec2 screen;
|
|
auto const position{editor_track::point_position(Track, m_track_point)};
|
|
m_track_snap = projection.project(position, screen) ? snap_free_end(position, {screen.x, screen.y}, &Track, Track.iCategoryFlag & 15, exclude) : editor_track::snap_target{};
|
|
}
|
|
|
|
void editor_mode::render_track_gizmo()
|
|
{
|
|
auto *track = selected_track();
|
|
if (track == nullptr || false == editable(*track))
|
|
{
|
|
m_track_gizmo_using = false;
|
|
return;
|
|
}
|
|
bool const pointmode = ui()->mode() == nodebank_panel::TRACK && m_track_point.valid() && m_track_point.path < static_cast<int>(track->m_paths.size());
|
|
bool const controlmode = pointmode && false == editor_track::is_end(m_track_point.kind);
|
|
|
|
gizmo_frame const frame;
|
|
auto &drag{m_track_gizmo_drag};
|
|
auto const operation{m_track_gizmo_using ? drag.operation : track_gizmo_operation(*track, pointmode)};
|
|
|
|
if (false == m_track_gizmo_using)
|
|
m_track_gizmo = track_gizmo_frame(*track, pointmode, operation);
|
|
|
|
glm::mat4 delta(1.0f);
|
|
auto gizmo{operation == gizmo_operation::scale ? ImGuizmo::SCALE_Z : ImGuizmo::TRANSLATE};
|
|
if (operation == gizmo_operation::rotate)
|
|
gizmo = ImGuizmo::ROTATE_X | ImGuizmo::ROTATE_Y | ImGuizmo::ROTATE_Z;
|
|
ImGuizmo::AllowAxisFlip(operation != gizmo_operation::scale);
|
|
frame.manipulate(gizmo, m_track_gizmo, operation == gizmo_operation::translate ? m_gizmo_snap : 0.0f, &delta, operation == gizmo_operation::translate ? ImGuizmo::WORLD : ImGuizmo::LOCAL);
|
|
ImGuizmo::AllowAxisFlip(true);
|
|
|
|
if (false == ImGuizmo::IsUsing())
|
|
{
|
|
if (m_track_gizmo_using)
|
|
track_gizmo_end(*track);
|
|
return;
|
|
}
|
|
if (false == m_track_gizmo_using)
|
|
{
|
|
track_gizmo_begin(*track, pointmode, operation);
|
|
if (operation == gizmo_operation::scale && false == mouse_along(drag.origin, drag.axis, drag.grabbed))
|
|
drag.grabbed = 0.0;
|
|
}
|
|
if (operation == gizmo_operation::rotate)
|
|
track_gizmo_rotated(*track);
|
|
else if (operation == gizmo_operation::scale)
|
|
track_gizmo_scaled(*track);
|
|
else if (controlmode)
|
|
track_gizmo_control(*track, frame.camera + glm::dvec3(m_track_gizmo[3]) - editor_track::point_position(*track, editor_track::end_of(m_track_point)));
|
|
else if (pointmode)
|
|
track_gizmo_moved_point(*track, frame.camera + glm::dvec3(m_track_gizmo[3]));
|
|
else if (glm::dvec3 const offset{delta[3]}; glm::length(offset) > 1e-9)
|
|
{
|
|
editor_track::translate(*track, offset);
|
|
editor_track::follow(*track, m_track_joints);
|
|
m_track_dirty = true;
|
|
}
|
|
track_gizmo_switches(*track);
|
|
commit_track_drag(false);
|
|
}
|