mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-19 10:49:20 +02:00
feat(editor): track gizmo rings along the track: direction, grade and cant
The rotate gizmo stands along the track. The cross ring changes the grade about the middle or the selected end, the joined tracks take the grade at the joints; the ring along the track changes the cant of the track or of the selected end, the joined tracks take it at the joints and ramp it. On a control vector the cross ring tilts it up or down. Ctrl steps by 5 degrees, 1 per mille, 5 mm.
This commit is contained in:
@@ -381,6 +381,42 @@ glm::mat4 gizmo_along(glm::dvec3 const &Position, glm::dvec3 const &Axis)
|
||||
return gizmo;
|
||||
}
|
||||
|
||||
void track_middle(TTrack const &Track, glm::dvec3 &Point, glm::dvec3 &Tangent, double &Chainage)
|
||||
{
|
||||
double total{0.0};
|
||||
for (auto const &path : Track.m_paths)
|
||||
total += bezier{path}.plan_length();
|
||||
Chainage = total * 0.5;
|
||||
double along{0.0};
|
||||
for (auto const &path : Track.m_paths)
|
||||
{
|
||||
bezier const curve{path};
|
||||
auto const length{curve.plan_length()};
|
||||
if (along + length >= Chainage || &path == &Track.m_paths.back())
|
||||
{
|
||||
auto const t{curve.parameter(Chainage - along)};
|
||||
Point = curve.point(t);
|
||||
Tangent = curve.first(t);
|
||||
return;
|
||||
}
|
||||
along += length;
|
||||
}
|
||||
Point = Track.location();
|
||||
Tangent = {0.0, 0.0, 1.0};
|
||||
}
|
||||
|
||||
glm::dvec3 level(glm::dvec3 const &Vector)
|
||||
{
|
||||
glm::dvec3 const flat{Vector.x, 0.0, Vector.z};
|
||||
return glm::length(flat) > 1e-9 ? glm::normalize(flat) : glm::dvec3{0.0, 0.0, 1.0};
|
||||
}
|
||||
|
||||
double roll_into(TTrack const &Track, editor_track::point_ref const &Point)
|
||||
{
|
||||
auto const &path{Track.m_paths[Point.path]};
|
||||
return Point.kind == editor_track::point_kind::start ? path.rolls[0] : -path.rolls[1];
|
||||
}
|
||||
|
||||
editor_track::point_ref stretch_remap(editor_track::point_ref Point, int const Path, std::size_t const Pieces)
|
||||
{
|
||||
if (false == Point.valid() || Point.path < Path)
|
||||
@@ -550,6 +586,19 @@ glm::mat4 editor_mode::track_gizmo_frame(TTrack const &Track, bool const Pointmo
|
||||
else if (stretch_end(Track, Pointmode ? m_track_point : editor_track::point_ref{}, path, atend))
|
||||
std::tie(anchor, axis) = stretch_axis(Track.m_paths[path], atend);
|
||||
}
|
||||
else if (Operation == gizmo_operation::rotate)
|
||||
{
|
||||
if (Pointmode && false == editor_track::is_end(m_track_point.kind))
|
||||
axis = level(anchor - editor_track::point_position(Track, editor_track::end_of(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
|
||||
{
|
||||
double chainage{0.0};
|
||||
track_middle(Track, anchor, axis, chainage);
|
||||
axis = level(axis);
|
||||
}
|
||||
}
|
||||
return gizmo_along(anchor - glm::dvec3{GfxRenderer->Camera_Position()}, axis);
|
||||
}
|
||||
|
||||
@@ -717,24 +766,120 @@ void editor_mode::track_gizmo_begin(TTrack &Track, bool const Pointmode, gizmo_o
|
||||
drag.length = bezier{Track.m_paths[drag.path]}.plan_length();
|
||||
}
|
||||
drag.stretch = drag.length;
|
||||
if (Operation == gizmo_operation::rotate)
|
||||
{
|
||||
auto const frame{track_gizmo_frame(Track, Pointmode, Operation)};
|
||||
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();
|
||||
}
|
||||
if (drag.point.valid() && editor_track::is_end(drag.point.kind) && drag.point.path < static_cast<int>(drag.chainages.size()))
|
||||
{
|
||||
drag.pivot_chainage = drag.chainages[drag.point.path] + (drag.point.kind == editor_track::point_kind::end ? drag.lengths[drag.point.path] : 0.0);
|
||||
drag.roll0 = drag.point.kind == editor_track::point_kind::start ? Track.m_paths[drag.point.path].rolls[0] : Track.m_paths[drag.point.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_turn(TTrack &Track, double const Angle)
|
||||
void editor_mode::track_gizmo_turn(TTrack &Track, int const Axis, double const Angle)
|
||||
{
|
||||
auto &drag{m_track_gizmo_drag};
|
||||
auto const step{std::remainder(Angle - drag.angle, 2.0 * glm::pi<double>())};
|
||||
if (std::abs(step) < 1e-9 || (drag.control && drag.fixed != glm::dvec3{}))
|
||||
if (drag.control && drag.fixed != glm::dvec3{})
|
||||
return;
|
||||
drag.angle += step;
|
||||
if (drag.control)
|
||||
auto const snap{Global.ctrlState};
|
||||
if (Axis == 1)
|
||||
{
|
||||
auto const control{editor_track::point_position(Track, drag.point) - editor_track::point_position(Track, editor_track::end_of(drag.point))};
|
||||
auto const plan{turned(plan_of(control), -step)};
|
||||
track_gizmo_control(Track, {plan.x, control.y, plan.y});
|
||||
auto target{Angle};
|
||||
if (snap)
|
||||
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::point_position(Track, drag.point) - editor_track::point_position(Track, editor_track::end_of(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;
|
||||
return;
|
||||
}
|
||||
editor_track::rotate_y(Track, drag.point.valid() ? editor_track::point_position(Track, drag.point) : m_track_pivot, step);
|
||||
editor_track::follow(Track, m_track_joints);
|
||||
if (Axis == 0)
|
||||
{
|
||||
auto target{-std::tan(std::clamp(Angle, -1.4, 1.4))};
|
||||
if (snap)
|
||||
target = std::round(target * 1000.0) / 1000.0;
|
||||
auto const step{target - drag.grade};
|
||||
if (std::abs(step) < 1e-9)
|
||||
return;
|
||||
drag.grade = target;
|
||||
if (drag.control)
|
||||
{
|
||||
auto const control{editor_track::point_position(Track, drag.point) - editor_track::point_position(Track, editor_track::end_of(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;
|
||||
}
|
||||
for (std::size_t i = 0; i < Track.m_paths.size() && 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::follow(Track, m_track_joints);
|
||||
editor_track::follow_grade(Track, m_track_joints);
|
||||
m_track_dirty = true;
|
||||
return;
|
||||
}
|
||||
if (drag.control)
|
||||
return;
|
||||
auto target{glm::degrees(Angle)};
|
||||
if (snap)
|
||||
target = glm::degrees(std::asin(std::clamp(std::round(1500.0 * std::sin(glm::radians(drag.roll0 + target)) / 5.0) * 5.0 / 1500.0, -1.0, 1.0))) - drag.roll0;
|
||||
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 == drag.point.valid() || (i == drag.point.path && drag.point.kind == editor_track::point_kind::start))
|
||||
path.rolls[0] += step;
|
||||
if (false == drag.point.valid() || (i == drag.point.path && drag.point.kind == editor_track::point_kind::end))
|
||||
path.rolls[1] += step;
|
||||
}
|
||||
for (auto const &joint : m_track_joints)
|
||||
{
|
||||
if (joint.own.path >= static_cast<int>(Track.m_paths.size()) || joint.theirs.path >= static_cast<int>(joint.other->m_paths.size()))
|
||||
continue;
|
||||
auto const roll{static_cast<float>(roll_into(Track, joint.own))};
|
||||
auto &theirs{joint.other->m_paths[joint.theirs.path]};
|
||||
if (joint.theirs.kind == editor_track::point_kind::start)
|
||||
theirs.rolls[0] = -roll;
|
||||
else
|
||||
theirs.rolls[1] = roll;
|
||||
}
|
||||
m_track_dirty = true;
|
||||
}
|
||||
|
||||
@@ -793,9 +938,11 @@ void editor_mode::render_track_gizmo()
|
||||
m_track_gizmo = track_gizmo_frame(*track, pointmode, operation);
|
||||
|
||||
glm::mat4 delta(1.0f);
|
||||
auto const gizmo{operation == gizmo_operation::rotate ? ImGuizmo::ROTATE_Y : operation == gizmo_operation::scale ? ImGuizmo::SCALE_Z : ImGuizmo::TRANSLATE};
|
||||
auto gizmo{operation == gizmo_operation::scale ? ImGuizmo::SCALE_Z : ImGuizmo::TRANSLATE};
|
||||
if (operation == gizmo_operation::rotate)
|
||||
gizmo = controlmode ? ImGuizmo::ROTATE_X | ImGuizmo::ROTATE_Y : ImGuizmo::ROTATE_X | ImGuizmo::ROTATE_Y | ImGuizmo::ROTATE_Z;
|
||||
ImGuizmo::AllowAxisFlip(operation != gizmo_operation::scale);
|
||||
frame.manipulate(gizmo, m_track_gizmo, operation == gizmo_operation::rotate ? 5.0f : operation == gizmo_operation::scale ? 0.0f : m_gizmo_snap, &delta, operation == gizmo_operation::scale ? ImGuizmo::LOCAL : ImGuizmo::WORLD);
|
||||
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 (ImGuizmo::IsUsing())
|
||||
@@ -808,7 +955,18 @@ void editor_mode::render_track_gizmo()
|
||||
}
|
||||
|
||||
if (operation == gizmo_operation::rotate)
|
||||
track_gizmo_turn(*track, std::atan2(static_cast<double>(m_track_gizmo[2][0]), static_cast<double>(m_track_gizmo[0][0])));
|
||||
{
|
||||
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]);
|
||||
}
|
||||
else if (operation == gizmo_operation::scale)
|
||||
{
|
||||
double along{0.0};
|
||||
@@ -3968,9 +4126,9 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
|
||||
{
|
||||
char const *action{"moves the point along the axes"};
|
||||
if (false == editor_track::is_end(m_track_point.kind))
|
||||
action = m_gizmo_op == gizmo_operation::rotate ? "turns the control vector, the joined track keeps the tangent" : m_gizmo_op == gizmo_operation::scale ? "length of the control vector" : "moves the control point, the joined track keeps the tangent";
|
||||
action = m_gizmo_op == gizmo_operation::rotate ? "turns the control vector sideways or up, the joined track keeps the tangent" : m_gizmo_op == gizmo_operation::scale ? "length of the control vector" : "moves the control point, the joined track keeps the tangent";
|
||||
else if (m_gizmo_op == gizmo_operation::rotate)
|
||||
action = "turns the track about the point, the joined tracks follow";
|
||||
action = "rings about the point: direction, grade, cant; the joined tracks follow";
|
||||
else if (m_gizmo_op == gizmo_operation::scale && selected != nullptr && track_gizmo_operation(*selected, true) == gizmo_operation::scale)
|
||||
action = "lengthens the track at this end, the curve keeps its radius";
|
||||
hints = {{"Drag a point", "moves it, an end dropped on a free end joins it"}, {"Gizmo", action}, {"Q W E", "gizmo: move, turn, lengthen"}, {"Esc", "releases the point"}};
|
||||
@@ -3980,7 +4138,7 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
|
||||
{
|
||||
char const *action{"moves the whole track"};
|
||||
if (m_gizmo_op == gizmo_operation::rotate)
|
||||
action = "turns the track, the joined tracks follow";
|
||||
action = "rings: direction, grade, cant; the joined tracks follow";
|
||||
else if (m_gizmo_op == gizmo_operation::scale && selected != nullptr && track_gizmo_operation(*selected, false) == gizmo_operation::scale)
|
||||
action = "lengthens the track at its free end, the curve keeps its radius";
|
||||
hints = {{"Drag a point", "moves it, an end dropped on a free end joins it"}, {"Gizmo", action}, {"Q W E", "gizmo: move, turn, lengthen"}, {"Shift+LMB", "adds a track to the selection, drag: a box"}, {"L", "new track, also from a free end"}, {"Esc", "deselects"}, {"Del", "deletes the track"}};
|
||||
@@ -4119,8 +4277,10 @@ std::string editor_mode::track_readout() const
|
||||
{
|
||||
auto const control{editor_track::point_position(*track, m_track_point) - editor_track::point_position(*track, editor_track::end_of(m_track_point))};
|
||||
auto text{format(STR_C("control %.2f m azimuth %.2f deg"), glm::length(control), azimuth(glm::dvec3{0.0}, control))};
|
||||
if (drag.operation == gizmo_operation::rotate)
|
||||
if (drag.operation == gizmo_operation::rotate && drag.ring == 1)
|
||||
text += format(STR_C(" turned %+.2f deg"), glm::degrees(drag.angle));
|
||||
if (drag.operation == gizmo_operation::rotate && drag.ring == 0)
|
||||
text += format(STR_C(" grade %+.1f per mille"), 1000.0 * grade_of(control) * (glm::dot(level(control), drag.basis[2]) >= 0.0 ? 1.0 : -1.0));
|
||||
if (drag.fixed != glm::dvec3{})
|
||||
text += "\n" + STR("along the switch");
|
||||
return text;
|
||||
@@ -4132,6 +4292,10 @@ std::string editor_mode::track_readout() const
|
||||
text += format(" R %.1f m", std::abs(drag.paths[drag.path].radius));
|
||||
return text;
|
||||
}
|
||||
if (drag.operation == gizmo_operation::rotate && drag.ring == 0)
|
||||
return format(STR_C("grade changed by %+.1f per mille"), 1000.0 * drag.grade);
|
||||
if (drag.operation == gizmo_operation::rotate && drag.ring == 2)
|
||||
return format(STR_C("cant %.0f mm (%+.0f mm)"), 1500.0 * std::sin(glm::radians(drag.roll0 + drag.roll)), 1500.0 * (std::sin(glm::radians(drag.roll0 + drag.roll)) - std::sin(glm::radians(drag.roll0))));
|
||||
if (drag.operation == gizmo_operation::rotate)
|
||||
return format(STR_C("turned %+.2f deg"), glm::degrees(drag.angle));
|
||||
if (m_track_point.valid() && m_track_point.path < static_cast<int>(track->m_paths.size()))
|
||||
|
||||
Reference in New Issue
Block a user