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,81 +931,99 @@ namespace K::UI {
} }
// Timeline // Timeline
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::TableSetColumnIndex(4);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{});
ImGui::TableNextColumn();
f32 init_y = ImGui::GetCursorScreenPos().y; f32 init_y = ImGui::GetCursorScreenPos().y;
if (!show_curve_editor) {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{});
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,
ImGui::PushID(0); l_out = l_ge_left_extr &&
if (l_in) { static_cast<f32>(current_layer.out) / static_cast<f32>(s.frame_max + 1) <=
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos); view_right;
static f32 l_in_old; f32 in_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.in,
ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666); s.frame_max),
ImGui::Button("##Layer_L", {layer_bound_width, view_height}); out_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.out,
if (ImGui::IsItemClicked()) { s.frame_max);
l_in_old = in_pos; ImGui::PushID(0);
if (l_in) {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos);
static f32 l_in_old;
ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666);
ImGui::Button("##Layer_L", {layer_bound_width, view_height});
if (ImGui::IsItemClicked()) {
l_in_old = in_pos;
}
if (ImGui::IsItemHovered()) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
}
if (ImGui::IsItemActive()) {
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);
}
ImGui::PopStyleColor();
ImGui::SameLine(0.0f, 0.0f);
} }
if (ImGui::IsItemHovered()) { if (l_le_right_extr && l_ge_left_extr) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); ImGui::Button(current_layer.name.c_str(),
{std::min(out_pos + fr_step, view_width) - static_cast<f32>(l_in) * in_pos -
layer_bound_width * static_cast<f32>(l_in + l_out), view_height});
static u32 in_old, out_old;
if (ImGui::IsItemClicked()) {
in_old = current_layer.in;
out_old = current_layer.out;
}
if (ImGui::IsItemActive()) {
i32 off = static_cast<i32>(std::trunc(ImGui::GetMouseDragDelta().x / fr_step));
off = std::max(-static_cast<i32>(in_old), off);
current_layer.in = in_old + off;
current_layer.out = out_old + off;
}
} }
if (ImGui::IsItemActive()) { if (l_out) {
current_layer.in = std::min(TimelineScreenViewToFrame(view_left, view_amt, view_width, static f32 r_out_old;
std::clamp(ImGui::GetMouseDragDelta().x + l_in_old, 0.0f, view_width), s.frame_max), current_layer.out); ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666);
ImGui::Button("##Layer_R", {layer_bound_width, view_height});
if (ImGui::IsItemClicked()) {
r_out_old = out_pos;
}
if (ImGui::IsItemHovered()) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
}
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);
}
ImGui::PopStyleColor();
} }
ImGui::PopStyleColor(); ImGui::PopID();
ImGui::SameLine(0.0f, 0.0f);
}
if (l_le_right_extr && l_ge_left_extr) {
ImGui::Button(current_layer.name.c_str(),
{std::min(out_pos + fr_step, view_width) - static_cast<f32>(l_in) * in_pos -
layer_bound_width * static_cast<f32>(l_in + l_out), view_height});
static u32 in_old, out_old;
if (ImGui::IsItemClicked()) {
in_old = current_layer.in;
out_old = current_layer.out;
}
if (ImGui::IsItemActive()) {
i32 off = static_cast<i32>(std::trunc(ImGui::GetMouseDragDelta().x / fr_step));
off = std::max(-static_cast<i32>(in_old), off);
current_layer.in = in_old + off;
current_layer.out = out_old + off;
}
}
if (l_out) {
static f32 r_out_old;
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666);
ImGui::Button("##Layer_R", {layer_bound_width, view_height});
if (ImGui::IsItemClicked()) {
r_out_old = out_pos;
}
if (ImGui::IsItemHovered()) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
}
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);
}
ImGui::PopStyleColor();
}
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,
if (ImGui::IsItemActive()) { row_height + style.CellPadding.y});
s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(mouse_tl_x, 0.0f, view_width), s.frame_max); 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);
}
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,46 +1098,57 @@ namespace K::UI {
ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(3);
ImGui::TableSetColumnIndex(4); ImGui::TableSetColumnIndex(4);
auto p = ImGui::GetCursorScreenPos(); // restored later if (!show_curve_editor) {
auto p = ImGui::GetCursorScreenPos(); // restored later
// plot uniform if connected // plot uniform if connected
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, {0.0f, 0.0f}); ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, {0.0f, 0.0f});
ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, {.1f, .1f}); ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, {.1f, .1f});
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 |
struct PlotInfo info { ImPlotFlags_NoInputs)) {
.begin = view_left * static_cast<f32>(s.frame_max + 1), ImPlot::SetupAxis(ImAxis_X1, "time",
.end = view_right * static_cast<f32>(s.frame_max + 1), ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight);
.samples = 100, ImPlot::SetupAxis(ImAxis_Y1, "val",
.s = s, ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight |
.connected_v = std::get<1>(connected_v) ImPlotAxisFlags_AutoFit);
}; struct PlotInfo info{
ImPlot::SetupAxisLimits(ImAxis_X1, info.begin, info.end, ImGuiCond_Always); .begin = view_left * static_cast<f32>(s.frame_max + 1),
ImPlot::SetupFinish(); .end = view_right * static_cast<f32>(s.frame_max + 1),
ImPlot::PlotLineG("v", [](int idx, void* user_data) -> ImPlotPoint { .samples = 100,
PlotInfo i = *(PlotInfo*)user_data; .s = s,
f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples); .connected_v = std::get<1>(connected_v)
CompositionState ss = i.s; };
ss.current_frame = static_cast<u64>(std::round(x)); ImPlot::SetupAxisLimits(ImAxis_X1, info.begin, info.end, ImGuiCond_Always);
bool good; ImPlot::SetupFinish();
Vector<Plugboard::ConnectInfo> info; ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint {
return {x, std::get<Plugboard::T_Map<Plugboard::T_Float>::type>(Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), Plugboard::T_Float, good))}; PlotInfo i = *(PlotInfo *) user_data;
f32 x = i.begin +
(i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples);
CompositionState ss = i.s;
ss.current_frame = static_cast<u64>(std::round(x));
bool good;
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))};
}, &info, info.samples); }, &info, info.samples);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
else { else {
ImGui::TextUnformatted("Constant"); ImGui::TextUnformatted("Constant");
} }
ImPlot::PopStyleColor(3); ImPlot::PopStyleColor(3);
ImPlot::PopStyleVar(2); ImPlot::PopStyleVar(2);
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,57 +1218,73 @@ 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);
ImVec2 begin_tl = ImGui::GetCursorScreenPos(); if (!show_curve_editor) {
ImVec2 begin_tl = ImGui::GetCursorScreenPos();
if (started_dragging) if (started_dragging)
chain_copy = chain; chain_copy = chain;
auto key_loop_target = &chain; auto key_loop_target = &chain;
if (dragging) { if (dragging) {
chain.segments.clear(); chain.segments.clear();
chain.selected.clear(); chain.selected.clear();
key_loop_target = &chain_copy; key_loop_target = &chain_copy;
}
for (u32 ii = 0; ii < key_loop_target->segments.size(); ii++) {
const auto& [k, val, segment] = (key_loop_target->segments)[ii];
ImGui::PushID(k);
auto sel_it = std::ranges::find(key_loop_target->selected, ii);
bool is_sel = sel_it != key_loop_target->selected.end();
i32 frame = k;
if (dragging && is_sel) {
frame += static_cast<i32>(ImGui::GetMouseDragDelta().x / tl_width * view_amt *
static_cast<f32>(s.frame_max + 1));
} }
ImVec2 draw_pos = {TimelineFrameToScreenView(view_left, view_amt, view_width, frame, for (u32 ii = 0; ii < key_loop_target->segments.size(); ii++) {
s.frame_max) + begin_tl.x - kf_tab_width / 2.0f, const auto &[k, val, segment] = (key_loop_target->segments)[ii];
begin_tl.y}; ImGui::PushID(k);
ImGui::SetCursorScreenPos(draw_pos); auto sel_it = std::ranges::find(key_loop_target->selected, ii);
bool is_sel = sel_it != key_loop_target->selected.end();
ImGui::Button(is_sel ? "s" : "k", {kf_tab_width, row_height * .8f}); i32 frame = k;
if (dragging && is_sel) {
frame += static_cast<i32>(ImGui::GetMouseDragDelta().x / tl_width *
view_amt *
static_cast<f32>(s.frame_max + 1));
}
ImVec2 draw_pos = {
TimelineFrameToScreenView(view_left, view_amt, view_width, frame,
s.frame_max) + begin_tl.x -
kf_tab_width / 2.0f,
begin_tl.y};
kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel, ImGui::SetCursorScreenPos(draw_pos);
sel_it, segment, val, ii, started_dragging, {draw_pos.x + kf_tab_width / 2.0f, draw_pos.y});
ImGui::PopID(); ImGui::Button(is_sel ? "s" : "k", {kf_tab_width, row_height * .8f});
}
for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) { kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel,
auto nit = std::next(it); sel_it, segment, val, ii, started_dragging,
if (nit != chain.segments.end() && it->value != nit->value) { {draw_pos.x + kf_tab_width / 2.0f, draw_pos.y});
window->DrawList->AddRectFilled({TimelineFrameToScreenView(view_left, view_amt, view_width, it->frame,
s.frame_max) + begin_tl.x + kf_tab_width / 2.0f, ImGui::PopID();
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, for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) {
begin_tl.y + row_height / 2.0f + 2.0f}, auto nit = std::next(it);
std::ranges::find(chain.selected, std::distance(chain.segments.begin(), it)) != chain.selected.end() && if (nit != chain.segments.end() && it->value != nit->value) {
std::ranges::find(chain.selected, std::distance(chain.segments.begin(), nit)) != chain.selected.end() ? 0x88FFAAAA : 0x44AAAAAA); window->DrawList->AddRectFilled(
{TimelineFrameToScreenView(view_left, view_amt, view_width,
it->frame,
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},
std::ranges::find(chain.selected,
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);
}
} }
}
ImGui::SetCursorScreenPos(begin_tl); ImGui::SetCursorScreenPos(begin_tl);
tl_bg_handler(); tl_bg_handler();
}
} }
} }
ImGui::PopID(); ImGui::PopID();
@ -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);
ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true); 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);
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();