some curve editor fixes

This commit is contained in:
lachrymaLF 2024-06-26 14:10:44 -04:00
parent cf0af93cac
commit 5ca9c91098

View file

@ -871,9 +871,11 @@ namespace K::UI {
std::erase(s.selected, i); std::erase(s.selected, i);
else else
s.selected.push_back(i); s.selected.push_back(i);
} else if (io.KeyShift) { }
else if (io.KeyShift) {
// TODO // TODO
} else { }
else {
s.selected.clear(); s.selected.clear();
if (!selected) if (!selected)
s.selected.push_back(i); s.selected.push_back(i);
@ -929,21 +931,28 @@ namespace K::UI {
} }
// Timeline // Timeline
ImGui::TableSetColumnIndex(4);
f32 init_y = ImGui::GetCursorScreenPos().y;
if (!show_curve_editor) {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{}); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{}); ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{});
ImGui::TableNextColumn();
f32 init_y = ImGui::GetCursorScreenPos().y;
const static f32 layer_bound_width = 5.0f; const static f32 layer_bound_width = 5.0f;
const bool l_le_right_extr = static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) <= view_right, const bool l_le_right_extr =
l_in = l_le_right_extr && static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) >= view_left, static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) <= view_right,
l_ge_left_extr = static_cast<f32>(current_layer.out) / static_cast<f32>(s.frame_max + 1) >= view_left, l_in = l_le_right_extr &&
l_out = l_ge_left_extr && static_cast<f32>(current_layer.out) / static_cast<f32>(s.frame_max + 1) <= view_right; static_cast<f32>(current_layer.in) / static_cast<f32>(s.frame_max + 1) >= view_left,
f32 in_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.in, s.frame_max), l_ge_left_extr =
out_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.out, s.frame_max); static_cast<f32>(current_layer.out) / static_cast<f32>(s.frame_max + 1) >= view_left,
l_out = l_ge_left_extr &&
static_cast<f32>(current_layer.out) / static_cast<f32>(s.frame_max + 1) <=
view_right;
f32 in_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.in,
s.frame_max),
out_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.out,
s.frame_max);
ImGui::PushID(0); ImGui::PushID(0);
if (l_in) { if (l_in) {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos);
@ -958,7 +967,10 @@ namespace K::UI {
} }
if (ImGui::IsItemActive()) { if (ImGui::IsItemActive()) {
current_layer.in = std::min(TimelineScreenViewToFrame(view_left, view_amt, view_width, current_layer.in = std::min(TimelineScreenViewToFrame(view_left, view_amt, view_width,
std::clamp(ImGui::GetMouseDragDelta().x + l_in_old, 0.0f, view_width), s.frame_max), current_layer.out); std::clamp(
ImGui::GetMouseDragDelta().x +
l_in_old, 0.0f, view_width),
s.frame_max), current_layer.out);
} }
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::SameLine(0.0f, 0.0f); ImGui::SameLine(0.0f, 0.0f);
@ -991,19 +1003,27 @@ namespace K::UI {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
} }
if (ImGui::IsItemActive()) { if (ImGui::IsItemActive()) {
current_layer.out = std::max(TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(ImGui::GetMouseDragDelta().x + r_out_old, 0.0f, view_width), s.frame_max), current_layer.in); current_layer.out = std::max(TimelineScreenViewToFrame(view_left, view_amt, view_width,
std::clamp(
ImGui::GetMouseDragDelta().x +
r_out_old, 0.0f, view_width),
s.frame_max), current_layer.in);
} }
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} }
ImGui::PopID(); ImGui::PopID();
ImGui::SetCursorScreenPos(ImVec2{tl_init_pos.x, init_y} - style.CellPadding); ImGui::SetCursorScreenPos(ImVec2{tl_init_pos.x, init_y} - style.CellPadding);
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y}); ImGui::InvisibleButton("##TL_BG", ImVec2{view_width + style.CellPadding.x * 2,
row_height + style.CellPadding.y});
if (ImGui::IsItemActive()) { if (ImGui::IsItemActive()) {
s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(mouse_tl_x, 0.0f, view_width), s.frame_max); s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width,
std::clamp(mouse_tl_x, 0.0f, view_width),
s.frame_max);
} }
ImGui::PopStyleVar(3); ImGui::PopStyleVar(3);
}
if (layer_open) { if (layer_open) {
i32 j = 0; i32 j = 0;
@ -1046,7 +1066,7 @@ namespace K::UI {
else if (ImGui::IsItemActive()) { else if (ImGui::IsItemActive()) {
if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) { if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) {
auto& c = s.plugboard.nodes.Store(Plugboard::Chain{}); auto& c = s.plugboard.nodes.Store(Plugboard::Chain{});
c.extra.chain.segments.emplace_back(s.current_frame, std::get<Plugboard::T_Map<Plugboard::T_Float>::type>(arg)); c.extra.chain.segments.emplace_back(s.current_frame, std::get<Plugboard::T_Map<Plugboard::T_Float>::type>(arg), Plugboard::InterpolationExtra(Plugboard::K_I_Linear));
c.extra.chain.selected.push_back(0); c.extra.chain.selected.push_back(0);
Plugboard::Connect(u.connection.p, u.connection.index, &c, 0); Plugboard::Connect(u.connection.p, u.connection.index, &c, 0);
Plugboard::Connect(&c, 0, &s.plugboard.in_instance, 0); Plugboard::Connect(&c, 0, &s.plugboard.in_instance, 0);
@ -1078,6 +1098,7 @@ namespace K::UI {
ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(3);
ImGui::TableSetColumnIndex(4); ImGui::TableSetColumnIndex(4);
if (!show_curve_editor) {
auto p = ImGui::GetCursorScreenPos(); // restored later auto p = ImGui::GetCursorScreenPos(); // restored later
// plot uniform if connected // plot uniform if connected
@ -1086,9 +1107,15 @@ namespace K::UI {
ImPlot::PushStyleColor(ImPlotCol_FrameBg, 0); ImPlot::PushStyleColor(ImPlotCol_FrameBg, 0);
ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0); ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0);
ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0); ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0);
if (connected_v.index() == 1 && ImPlot::BeginPlot("uniform", {view_width, row_height}, ImPlotFlags_CanvasOnly | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) { if (connected_v.index() == 1 && ImPlot::BeginPlot("uniform", {view_width, row_height},
ImPlot::SetupAxis(ImAxis_X1, "time", ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight); ImPlotFlags_CanvasOnly |
ImPlot::SetupAxis(ImAxis_Y1, "val", ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_AutoFit); ImPlotFlags_NoFrame |
ImPlotFlags_NoInputs)) {
ImPlot::SetupAxis(ImAxis_X1, "time",
ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight);
ImPlot::SetupAxis(ImAxis_Y1, "val",
ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight |
ImPlotAxisFlags_AutoFit);
struct PlotInfo info{ struct PlotInfo info{
.begin = view_left * static_cast<f32>(s.frame_max + 1), .begin = view_left * static_cast<f32>(s.frame_max + 1),
.end = view_right * static_cast<f32>(s.frame_max + 1), .end = view_right * static_cast<f32>(s.frame_max + 1),
@ -1100,12 +1127,15 @@ namespace K::UI {
ImPlot::SetupFinish(); ImPlot::SetupFinish();
ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint { ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint {
PlotInfo i = *(PlotInfo *) user_data; PlotInfo i = *(PlotInfo *) user_data;
f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples); f32 x = i.begin +
(i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples);
CompositionState ss = i.s; CompositionState ss = i.s;
ss.current_frame = static_cast<u64>(std::round(x)); ss.current_frame = static_cast<u64>(std::round(x));
bool good; bool good;
Vector<Plugboard::ConnectInfo> info; Vector<Plugboard::ConnectInfo> info;
return {x, std::get<Plugboard::T_Map<Plugboard::T_Float>::type>(Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), Plugboard::T_Float, good))}; return {x, std::get<Plugboard::T_Map<Plugboard::T_Float>::type>(
Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v),
Plugboard::T_Float, good))};
}, &info, info.samples); }, &info, info.samples);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
@ -1118,6 +1148,7 @@ namespace K::UI {
ImGui::SetCursorScreenPos(p); ImGui::SetCursorScreenPos(p);
tl_bg_handler(); tl_bg_handler();
}
if (uniform_open && connected_v.index() == 1) { if (uniform_open && connected_v.index() == 1) {
for (const auto& info: s.plugboard.show_nodes[u.connection.index]) { for (const auto& info: s.plugboard.show_nodes[u.connection.index]) {
@ -1142,9 +1173,6 @@ namespace K::UI {
auto& [chain, chain_copy] = std::get<Plugboard::Chain*>(info.p)->extra; auto& [chain, chain_copy] = std::get<Plugboard::Chain*>(info.p)->extra;
if (tl_clear_selection_request)
chain.selected.clear();
bool m_empty = chain.segments.empty(), m_has_before = !m_empty && chain.segments.begin()->frame < s.current_frame; bool m_empty = chain.segments.empty(), m_has_before = !m_empty && chain.segments.begin()->frame < s.current_frame;
ImGui::BeginDisabled(m_empty || !m_has_before); ImGui::BeginDisabled(m_empty || !m_has_before);
@ -1190,6 +1218,7 @@ namespace K::UI {
ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(3);
ImGui::Text("%lu", chain.segments.size()); ImGui::Text("%lu", chain.segments.size());
ImGui::TableSetColumnIndex(4); ImGui::TableSetColumnIndex(4);
if (!show_curve_editor) {
ImVec2 begin_tl = ImGui::GetCursorScreenPos(); ImVec2 begin_tl = ImGui::GetCursorScreenPos();
if (started_dragging) if (started_dragging)
@ -1210,11 +1239,14 @@ namespace K::UI {
i32 frame = k; i32 frame = k;
if (dragging && is_sel) { if (dragging && is_sel) {
frame += static_cast<i32>(ImGui::GetMouseDragDelta().x / tl_width * view_amt * frame += static_cast<i32>(ImGui::GetMouseDragDelta().x / tl_width *
view_amt *
static_cast<f32>(s.frame_max + 1)); static_cast<f32>(s.frame_max + 1));
} }
ImVec2 draw_pos = {TimelineFrameToScreenView(view_left, view_amt, view_width, frame, ImVec2 draw_pos = {
s.frame_max) + begin_tl.x - kf_tab_width / 2.0f, TimelineFrameToScreenView(view_left, view_amt, view_width, frame,
s.frame_max) + begin_tl.x -
kf_tab_width / 2.0f,
begin_tl.y}; begin_tl.y};
ImGui::SetCursorScreenPos(draw_pos); ImGui::SetCursorScreenPos(draw_pos);
@ -1222,20 +1254,31 @@ namespace K::UI {
ImGui::Button(is_sel ? "s" : "k", {kf_tab_width, row_height * .8f}); ImGui::Button(is_sel ? "s" : "k", {kf_tab_width, row_height * .8f});
kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel, kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel,
sel_it, segment, val, ii, started_dragging, {draw_pos.x + kf_tab_width / 2.0f, draw_pos.y}); sel_it, segment, val, ii, started_dragging,
{draw_pos.x + kf_tab_width / 2.0f, draw_pos.y});
ImGui::PopID(); ImGui::PopID();
} }
for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) { for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) {
auto nit = std::next(it); auto nit = std::next(it);
if (nit != chain.segments.end() && it->value != nit->value) { if (nit != chain.segments.end() && it->value != nit->value) {
window->DrawList->AddRectFilled({TimelineFrameToScreenView(view_left, view_amt, view_width, it->frame, window->DrawList->AddRectFilled(
s.frame_max) + begin_tl.x + kf_tab_width / 2.0f, {TimelineFrameToScreenView(view_left, view_amt, view_width,
begin_tl.y + row_height / 2.0f - 2.0f}, {TimelineFrameToScreenView(view_left, view_amt, view_width, nit->frame, it->frame,
s.frame_max) + begin_tl.x - kf_tab_width / 2.0f, s.frame_max) + begin_tl.x +
kf_tab_width / 2.0f,
begin_tl.y + row_height / 2.0f - 2.0f},
{TimelineFrameToScreenView(view_left, view_amt, view_width,
nit->frame,
s.frame_max) + begin_tl.x -
kf_tab_width / 2.0f,
begin_tl.y + row_height / 2.0f + 2.0f}, begin_tl.y + row_height / 2.0f + 2.0f},
std::ranges::find(chain.selected, std::distance(chain.segments.begin(), it)) != chain.selected.end() && std::ranges::find(chain.selected,
std::ranges::find(chain.selected, std::distance(chain.segments.begin(), nit)) != chain.selected.end() ? 0x88FFAAAA : 0x44AAAAAA); std::distance(chain.segments.begin(), it)) !=
chain.selected.end() &&
std::ranges::find(chain.selected,
std::distance(chain.segments.begin(), nit)) !=
chain.selected.end() ? 0x88FFAAAA : 0x44AAAAAA);
} }
} }
@ -1243,6 +1286,7 @@ namespace K::UI {
tl_bg_handler(); tl_bg_handler();
} }
} }
}
ImGui::PopID(); ImGui::PopID();
j++; j++;
} }
@ -1266,8 +1310,6 @@ namespace K::UI {
if (started_dragging && dragging) if (started_dragging && dragging)
started_dragging = false; started_dragging = false;
if (tl_clear_selection_request_this_frame && tl_clear_selection_request)
tl_clear_selection_request = false;
tl_end_begin = {tl_init_pos.x, ImGui::GetCursorScreenPos().y}; tl_end_begin = {tl_init_pos.x, ImGui::GetCursorScreenPos().y};
@ -1330,7 +1372,7 @@ namespace K::UI {
ImPlot::PushStyleColor(ImPlotCol_FrameBg, 0); ImPlot::PushStyleColor(ImPlotCol_FrameBg, 0);
ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0); ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0);
ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0); ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0);
if (ImPlot::BeginPlot("uniform", {view_width, ImGui::GetContentRegionAvail().y}, ImPlotFlags_CanvasOnly | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) { if (ImPlot::BeginPlot("uniform", {view_width, ImGui::GetContentRegionAvail().y}, ImPlotFlags_NoTitle | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) {
static bool started_dragging = false, dragging = false; static bool started_dragging = false, dragging = false;
ImPlot::SetupAxis(ImAxis_X1, "time", ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines); ImPlot::SetupAxis(ImAxis_X1, "time", ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines);
@ -1354,6 +1396,7 @@ namespace K::UI {
if (!std::holds_alternative<Plugboard::Chain*>(n)) if (!std::holds_alternative<Plugboard::Chain*>(n))
continue; continue;
ImGui::PushID(&n);
auto& [chain, chain_copy] = std::get<Plugboard::Chain*>(n)->extra; auto& [chain, chain_copy] = std::get<Plugboard::Chain*>(n)->extra;
Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out
@ -1391,7 +1434,11 @@ namespace K::UI {
f64 x = frame, y = v; f64 x = frame, y = v;
ImVec2 k_pos = ImPlot::PlotToPixels(x, y); ImVec2 k_pos = ImPlot::PlotToPixels(x, y);
const bool is_last = ii + 1 == key_loop_target->segments.size();
if (is_last)
ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true); ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true);
else
ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true, "%.2f, %.2f, %s", x, y, Plugboard::K_Interpolation_Names[segment.interp]);
ImGui::SetCursorScreenPos(k_pos - ImVec2{10.0f / 2.0f, 10.0f / 2.0f}); ImGui::SetCursorScreenPos(k_pos - ImVec2{10.0f / 2.0f, 10.0f / 2.0f});
ImGui::PushStyleColor(ImGuiCol_Button, is_sel ? 0xFFFFFF88 : 0xFFAA7777); ImGui::PushStyleColor(ImGuiCol_Button, is_sel ? 0xFFFFFF88 : 0xFFAA7777);
@ -1401,7 +1448,7 @@ namespace K::UI {
kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel, kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel,
sel_it, segment, v, ii, started_dragging, k_pos); sel_it, segment, v, ii, started_dragging, k_pos);
if (segment.interp == Plugboard::K_I_CubicBezier && ii + 1 < key_loop_target->segments.size()) { if (segment.interp == Plugboard::K_I_CubicBezier && !is_last) {
const auto& [k_nxt, val_nxt, _] = (key_loop_target->segments)[ii + 1]; const auto& [k_nxt, val_nxt, _] = (key_loop_target->segments)[ii + 1];
i32 frame_nxt = k_nxt; i32 frame_nxt = k_nxt;
f64 v_nxt = val_nxt; f64 v_nxt = val_nxt;
@ -1451,10 +1498,10 @@ namespace K::UI {
f64 xs[2] = {static_cast<f64>(frame), p2x}, f64 xs[2] = {static_cast<f64>(frame), p2x},
ys[2] = {v, p2y}; ys[2] = {v, p2y};
ImPlot::PlotLine("cb tangent", xs, ys, 2); ImPlot::PlotLine("###cb tangent", xs, ys, 2);
xs[0] = p3x; xs[1] = static_cast<f64>(frame_nxt); xs[0] = p3x; xs[1] = static_cast<f64>(frame_nxt);
ys[0] = p3y; ys[1] = v_nxt; ys[0] = p3y; ys[1] = v_nxt;
ImPlot::PlotLine("cb tangent", xs, ys, 2); ImPlot::PlotLine("###cb tangent", xs, ys, 2);
} }
ImGui::PopID(); ImGui::PopID();
@ -1467,7 +1514,7 @@ namespace K::UI {
.s = s, .s = s,
.connected_v = i .connected_v = i
}; };
ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint { ImPlot::PlotLineG(s.plugboard.nodes.GetName(static_cast<Plugboard::K_P_Nodes>(n.index())), [](int idx, void *user_data) -> ImPlotPoint {
PlotInfo i = *(PlotInfo *) user_data; PlotInfo i = *(PlotInfo *) user_data;
f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples); f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples);
CompositionState ss = i.s; CompositionState ss = i.s;
@ -1478,6 +1525,7 @@ namespace K::UI {
Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v),
Plugboard::T_Float, good))}; Plugboard::T_Float, good))};
}, &info, info.samples); }, &info, info.samples);
ImGui::PopID();
} }
ImPlot::EndPlot(); ImPlot::EndPlot();
@ -1554,6 +1602,15 @@ namespace K::UI {
} }
} }
} }
if (tl_clear_selection_request)
for (auto& p : s.plugboard.selected_nodes)
if (auto *n = std::get_if<Plugboard::Chain*>(&p)){
auto& [chain, _] = (*n)->extra;
chain.selected.clear();
}
if (tl_clear_selection_request_this_frame && tl_clear_selection_request)
tl_clear_selection_request = false;
} }
ImGui::EndChild(); ImGui::EndChild();