good graph editor
This commit is contained in:
parent
d639ee8b9b
commit
b80a0db027
4 changed files with 281 additions and 122 deletions
|
@ -130,13 +130,13 @@ namespace K {
|
|||
const bgfx::Stats *stat = bgfx::getStats();
|
||||
const bgfx::Caps *caps = bgfx::getCaps();
|
||||
bgfx::dbgTextClear();
|
||||
bgfx::dbgTextPrintf(0, app_state.window_height/16 - 4, 0xf8,
|
||||
bgfx::dbgTextPrintf(0, app_state.window_height/16 - 8, 0xf8,
|
||||
" lachrymal.net :: %s Renderer :: Max Views %u :: Max FBs %u :: Max Texture Samplers %u :: Blitting %s :: %u FPS",
|
||||
caps->supported & BGFX_CAPS_RENDERER_MULTITHREADED ? "Multithreaded" : "Singlethreaded",
|
||||
caps->limits.maxViews, caps->limits.maxFrameBuffers,
|
||||
caps->limits.maxTextureSamplers, caps->supported & BGFX_CAPS_TEXTURE_BLIT ? "OK" : "NO",
|
||||
stat->cpuTimerFreq / stat->cpuTimeFrame);
|
||||
bgfx::dbgTextPrintf(0, app_state.window_height/16 - 3, 0xf8,
|
||||
bgfx::dbgTextPrintf(0, app_state.window_height/16 - 7, 0xf8,
|
||||
" Project Keishiki :: %s :: Build %s %s :: SDL %i.%i.%i :: bgfx 1.%i :: Dear ImGui %s :: %s",
|
||||
BX_COMPILER_NAME, __DATE__, __TIME__, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
|
||||
BGFX_API_VERSION, ImGui::GetVersion(), bgfx::getRendererName(bgfx::getRendererType()));
|
||||
|
|
371
Keishiki/UI.cpp
371
Keishiki/UI.cpp
|
@ -403,6 +403,13 @@ namespace K::UI {
|
|||
return std::clamp(static_cast<u64>(std::round((view / view_width * view_amt + view_left) * static_cast<f32>(frame_max + 1))), static_cast<u64>(0), frame_max);
|
||||
}
|
||||
|
||||
struct PlotInfo {
|
||||
f32 begin, end;
|
||||
i32 samples;
|
||||
const CompState &s;
|
||||
PlugboardGraph::ConnectInfo &connected_v;
|
||||
};
|
||||
|
||||
void Composition(CompState& s) {
|
||||
if (!ImGui::Begin("Composition", &draw_comp, ImGuiWindowFlags_NoScrollbar)) {
|
||||
ImGui::End();
|
||||
|
@ -481,8 +488,9 @@ namespace K::UI {
|
|||
ImGui::Checkbox("Chain Editor", &show_curve_editor);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImRect drag_rect{io.MousePos, io.MousePos};
|
||||
drag_rect.Add(io.MouseClickedPos[ImGuiMouseButton_Left]);
|
||||
ImGuiStyle style = ImGui::GetStyle();
|
||||
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||
|
||||
static f32 view_scale = 1.0f;
|
||||
static ImVec2 view_pos{}, old_view_pos{}, nodes_begin{};
|
||||
|
@ -491,8 +499,8 @@ namespace K::UI {
|
|||
ImVec2 drag_source{};
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF100500);
|
||||
ImGui::SetNextWindowSizeConstraints({avail.x * .1f, -1}, { avail.x * .8f, -1 });
|
||||
if (ImGui::BeginChild("Nodes", {avail.x * .4f, avail.y}, ImGuiChildFlags_ResizeX, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar)) {
|
||||
ImGui::SetNextWindowSizeConstraints({ImGui::GetContentRegionAvail().x * .1f, -1}, { ImGui::GetContentRegionAvail().x * .8f, -1 });
|
||||
if (ImGui::BeginChild("Nodes", {ImGui::GetContentRegionAvail().x * .4f, ImGui::GetContentRegionAvail().y}, ImGuiChildFlags_ResizeX, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar)) {
|
||||
static u32 selected;
|
||||
|
||||
ImGuiWindow *window = ImGui::GetCurrentWindow();
|
||||
|
@ -548,6 +556,7 @@ namespace K::UI {
|
|||
|
||||
ImGui::SameLine(0.0f, 0.5f);
|
||||
|
||||
// Timeline setup
|
||||
f32 table_left = ImGui::GetCursorScreenPos().x;
|
||||
ImVec2 tl_init_pos, tl_end_begin;
|
||||
f32 view_width,
|
||||
|
@ -562,17 +571,27 @@ namespace K::UI {
|
|||
f32 view_amt = view_right - view_left, mouse_tl_x;
|
||||
f32 fr_step;
|
||||
|
||||
static Vector<PlugboardGraph::NodeInstance*> selected_chains{};
|
||||
|
||||
// Mouse Handlers
|
||||
// Drag playhead/pan
|
||||
static bool tl_clear_selection_request = false;
|
||||
bool tl_clear_selection_request_this_frame = tl_clear_selection_request;
|
||||
auto draw_tl_bg_drag_area = [&view_width, &style, &view_amt, &s, &mouse_tl_x](f32 h = 0.0f) {
|
||||
static bool bg_drag_select_active = false;
|
||||
static Vector<PlugboardNodes::ChainSegment*> keys_selected_by_bg_drag{};
|
||||
auto draw_tl_bg_drag_area = [&view_width, &style, &view_amt, &s, &mouse_tl_x, &io](f32 h = 0.0f, f32 w = 0.0f) {
|
||||
static f32 pan_x, view_left_old, view_right_old;
|
||||
ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, h == 0.0f ? row_height + style.CellPadding.y : h});
|
||||
ImGui::InvisibleButton("##TL_BG", ImVec2{ w == 0.0f ? view_width + style.CellPadding.x * 2 : w, h == 0.0f ? row_height + style.CellPadding.y : h});
|
||||
if (ImGui::IsItemClicked()) {
|
||||
tl_clear_selection_request = true;
|
||||
tl_clear_selection_request = !(io.KeyCtrl || io.KeyShift);
|
||||
bg_drag_select_active = !io.KeyShift;
|
||||
}
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||
bg_drag_select_active = false;
|
||||
keys_selected_by_bg_drag.clear();
|
||||
}
|
||||
if (ImGui::IsItemActive()) {
|
||||
if (io.KeyShift)
|
||||
s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(mouse_tl_x, 0.0f, view_width), s.frame_max);
|
||||
}
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) {
|
||||
|
@ -596,25 +615,60 @@ namespace K::UI {
|
|||
}
|
||||
};
|
||||
|
||||
// Zoom
|
||||
auto tl_scroll_zoom = [&mouse_tl_x, &view_width, &io, &view_amt]() {
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && mouse_tl_x >= 0 && mouse_tl_x < view_width && io.MouseWheel != 0.0f) {
|
||||
f32 center = mouse_tl_x / view_width * view_amt + view_left;
|
||||
f32 new_view_amt = std::clamp(view_amt - 0.05f * io.MouseWheel, 0.05f, 1.0f);
|
||||
view_left = center - new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt;
|
||||
view_right = center + new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt;
|
||||
if (view_left < 0.0f) {
|
||||
view_right -= view_left;
|
||||
view_left = 0.0f;
|
||||
constexpr static f32 knob_width = 8.0f;
|
||||
constexpr static f32 kf_tab_width = 6.0f;
|
||||
|
||||
auto kf_drag_btn_handler = [&io, &drag_rect](
|
||||
PlugboardNodes::ChainSel *key_loop_target, bool dragging, PlugboardNodes::ChainSel& chain, i32 frame, bool is_sel,
|
||||
Vector<u32>::iterator sel_it, const PlugboardNodes::InterpolationExtra& segment, f64 v, u32 ii, bool& started_dragging, const ImVec2& k_pos) {
|
||||
if (dragging) {
|
||||
auto pos = std::lower_bound(chain.segments.begin(), chain.segments.end(), frame,
|
||||
[](const PlugboardNodes::ChainSegment &a, i32 b) {
|
||||
return a.frame < b;
|
||||
});
|
||||
|
||||
auto pos_index = std::distance(chain.segments.begin(), pos);
|
||||
|
||||
if (pos != chain.segments.end() && pos->frame == frame) { // overlapping kf's
|
||||
if (is_sel)
|
||||
pos->value = static_cast<f32>(v);
|
||||
} else {
|
||||
for (auto &sel: chain.selected) {
|
||||
if (sel >= pos_index)
|
||||
sel++;
|
||||
}
|
||||
else if (view_right > 1.0f) {
|
||||
view_left -= (view_right - 1.0f);
|
||||
view_right = 1.0f;
|
||||
chain.segments.emplace(pos, frame, v, segment);
|
||||
}
|
||||
|
||||
if (is_sel)
|
||||
chain.selected.push_back(pos_index);
|
||||
} else {
|
||||
if (ImGui::IsItemClicked()) {
|
||||
started_dragging = true; // setup dragging & start on next frame
|
||||
|
||||
if (!is_sel || ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
if (!io.KeyCtrl)
|
||||
chain.selected.clear();
|
||||
chain.selected.push_back(ii);
|
||||
}
|
||||
}
|
||||
if (!is_sel && bg_drag_select_active &&
|
||||
drag_rect.Contains(k_pos)) {
|
||||
keys_selected_by_bg_drag.push_back(
|
||||
&(key_loop_target->segments)[ii]); // This sucks pretty bad...
|
||||
chain.selected.push_back(ii);
|
||||
}
|
||||
auto bg_drag_sel_it = std::find(keys_selected_by_bg_drag.begin(),
|
||||
keys_selected_by_bg_drag.end(),
|
||||
&(key_loop_target->segments)[ii]);
|
||||
if (is_sel && bg_drag_sel_it != keys_selected_by_bg_drag.end() &&
|
||||
!drag_rect.Contains(k_pos)) {
|
||||
chain.selected.erase(sel_it);
|
||||
keys_selected_by_bg_drag.erase(bg_drag_sel_it);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr static f32 knob_width = 8.0f;
|
||||
if (ImGui::BeginTable("Layers", 5, ImGuiTableFlags_BordersInner | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
|
||||
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_IndentDisable);
|
||||
|
@ -704,7 +758,6 @@ namespace K::UI {
|
|||
ImGui::TableHeader("");
|
||||
|
||||
// Main rows
|
||||
tl_scroll_zoom();
|
||||
i32 move_from = -1, move_to = -1;
|
||||
bool after{};
|
||||
|
||||
|
@ -929,7 +982,7 @@ namespace K::UI {
|
|||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
|
||||
|
||||
if (connected_v.index() == 0) {
|
||||
if (connected_v.index() == 0)
|
||||
std::visit([](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>)
|
||||
|
@ -943,7 +996,7 @@ namespace K::UI {
|
|||
else if constexpr (std::is_same_v<T, PlugboardGraph::T_Map<PlugboardGraph::T_XYZ>::type>)
|
||||
ImGui::DragFloat3("##", &arg.x, 0.005f);
|
||||
}, std::get<0>(connected_v));
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
auto p = ImGui::GetCursorScreenPos();
|
||||
|
@ -957,11 +1010,10 @@ namespace K::UI {
|
|||
if (connected_v.index() == 1 && ImPlot::BeginPlot("uniform", {view_width, row_height}, ImPlotFlags_CanvasOnly | 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 { f32 begin, end; i32 samples; const CompState &s; PlugboardGraph::ConnectInfo &connected_v; };
|
||||
struct PlotInfo info {
|
||||
.begin = view_left * static_cast<f32>(s.frame_max + 1),
|
||||
.end = view_right * static_cast<f32>(s.frame_max + 1),
|
||||
.samples = 200,
|
||||
.samples = 100,
|
||||
.s = s,
|
||||
.connected_v = std::get<1>(connected_v)
|
||||
};
|
||||
|
@ -994,10 +1046,19 @@ namespace K::UI {
|
|||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Indent();
|
||||
ImGui::TreeNodeEx(info.p->node->name.c_str(),
|
||||
ImGuiTreeNodeFlags_SpanFullWidth |
|
||||
auto n_flags = ImGuiTreeNodeFlags_SpanFullWidth |
|
||||
ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Leaf |
|
||||
ImGuiTreeNodeFlags_FramePadding);
|
||||
ImGuiTreeNodeFlags_FramePadding;
|
||||
auto chain_sel_it = std::find(selected_chains.begin(), selected_chains.end(), info.p);
|
||||
if (chain_sel_it != selected_chains.end())
|
||||
n_flags |= ImGuiTreeNodeFlags_Selected;
|
||||
ImGui::TreeNodeEx(info.p->node->name.c_str(), n_flags);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
if (chain_sel_it != selected_chains.end())
|
||||
selected_chains.erase(chain_sel_it);
|
||||
else
|
||||
selected_chains.push_back(info.p);
|
||||
}
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
|
||||
auto& [chain, chain_copy] = *std::any_cast<PlugboardNodes::ChainExtra>(&info.p->extra);
|
||||
|
@ -1022,6 +1083,9 @@ namespace K::UI {
|
|||
f32 v = std::get<PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>(
|
||||
PlugboardGraph::Eval(s, info));
|
||||
if (ImGui::DragFloat("##value", &v, 0.005f)) {
|
||||
if (std::find(selected_chains.begin(), selected_chains.end(), info.p) == selected_chains.end())
|
||||
selected_chains.push_back(info.p);
|
||||
|
||||
auto l = std::lower_bound(chain.segments.begin(), chain.segments.end(), s.current_frame,
|
||||
[](const PlugboardNodes::ChainSegment& a, i32 b) { return a.frame < b; });
|
||||
if (l != chain.segments.end() && l->frame == static_cast<i32>(s.current_frame))
|
||||
|
@ -1052,7 +1116,6 @@ namespace K::UI {
|
|||
if (started_dragging)
|
||||
chain_copy = chain;
|
||||
|
||||
const static f32 kf_tab_width = 6.0f;
|
||||
auto key_loop_target = &chain;
|
||||
if (dragging) {
|
||||
chain.segments.clear();
|
||||
|
@ -1071,48 +1134,17 @@ namespace K::UI {
|
|||
frame += static_cast<i32>(ImGui::GetMouseDragDelta().x / tl_width * view_amt *
|
||||
static_cast<f32>(s.frame_max + 1));
|
||||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(
|
||||
{TimelineFrameToScreenView(view_left, view_amt, view_width, frame,
|
||||
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});
|
||||
begin_tl.y};
|
||||
|
||||
ImGui::SetCursorScreenPos(draw_pos);
|
||||
|
||||
ImGui::Button(is_sel ? "s" : "k", {kf_tab_width, row_height * .8f});
|
||||
|
||||
if (dragging) {
|
||||
auto pos = std::lower_bound(chain.segments.begin(), chain.segments.end(), frame,
|
||||
[](const PlugboardNodes::ChainSegment& a, i32 b) {
|
||||
return a.frame < b;
|
||||
});
|
||||
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});
|
||||
|
||||
auto pos_index = std::distance(chain.segments.begin(), pos);
|
||||
|
||||
if (pos != chain.segments.end() && pos->frame == frame) { // overlapping kf's
|
||||
if (is_sel)
|
||||
pos->value = val;
|
||||
}
|
||||
else {
|
||||
for (auto &sel: chain.selected) {
|
||||
if (sel >= pos_index)
|
||||
sel++;
|
||||
}
|
||||
chain.segments.emplace(pos, frame, val, segment);
|
||||
}
|
||||
|
||||
if (is_sel)
|
||||
chain.selected.push_back(pos_index);
|
||||
}
|
||||
else {
|
||||
if (ImGui::IsItemClicked()) {
|
||||
started_dragging = true; // setup dragging & start on next frame
|
||||
|
||||
if (!is_sel || ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
if (!io.KeyCtrl)
|
||||
chain.selected.clear();
|
||||
chain.selected.push_back(ii);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) {
|
||||
|
@ -1184,7 +1216,7 @@ namespace K::UI {
|
|||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(nodes_begin);
|
||||
if (ImGui::BeginChild("Overlay", {table_left - ImGui::GetWindowPos().x, 0.0f}, false, ImGuiWindowFlags_NoInputs)) {
|
||||
if (ImGui::BeginChild("Node Connection Overlay", {table_left - ImGui::GetWindowPos().x, 0.0f}, false, ImGuiWindowFlags_NoInputs)) {
|
||||
auto *window = ImGui::GetCurrentWindow();
|
||||
for (auto &[_, link]: s.plugboard.links_pos)
|
||||
for (auto &sink: link.sinks)
|
||||
|
@ -1201,7 +1233,6 @@ namespace K::UI {
|
|||
ImGui::SetCursorScreenPos(tl_end_begin);
|
||||
if (ImGui::BeginChild("TL Bottom Unfilled Drag Overlay")) {
|
||||
draw_tl_bg_drag_area(ImGui::GetContentRegionAvail().y);
|
||||
tl_scroll_zoom();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
|
@ -1210,8 +1241,6 @@ namespace K::UI {
|
|||
ImGui::SetCursorScreenPos(tl_grid_cursor_pos);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, 0xFF111111);
|
||||
if (ImGui::BeginChild("Curve Editor", ImGui::GetContentRegionAvail())) {
|
||||
draw_tl_bg_drag_area(ImGui::GetContentRegionAvail().y);
|
||||
tl_scroll_zoom();
|
||||
ImGui::SetCursorScreenPos(tl_grid_cursor_pos);
|
||||
|
||||
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, {0.0f, 0.0f});
|
||||
|
@ -1220,48 +1249,148 @@ namespace K::UI {
|
|||
ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0);
|
||||
ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0);
|
||||
if (ImPlot::BeginPlot("uniform", {view_width, ImGui::GetContentRegionAvail().y}, ImPlotFlags_CanvasOnly | 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 { f32 begin, end; i32 samples; const CompState &s; PlugboardGraph::ConnectInfo &connected_v; };
|
||||
// struct PlotInfo info {
|
||||
// .begin = view_left * static_cast<f32>(s.frame_max + 1),
|
||||
// .end = view_right * static_cast<f32>(s.frame_max + 1),
|
||||
// .samples = 200,
|
||||
// .s = s,
|
||||
// .connected_v = std::get<1>(connected_v)
|
||||
// };
|
||||
// ImPlot::SetupAxisLimits(ImAxis_X1, info.begin, info.end, ImGuiCond_Always);
|
||||
// ImPlot::SetupFinish();
|
||||
// ImPlot::PlotLineG("v", [](int idx, void* user_data) -> ImPlotPoint {
|
||||
// PlotInfo i = *(PlotInfo*)user_data;
|
||||
// f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples);
|
||||
// CompState ss = i.s;
|
||||
// ss.current_frame = static_cast<u64>(std::round(x));
|
||||
// bool good;
|
||||
// Vector<PlugboardGraph::ConnectInfo> info;
|
||||
// return {x, std::get<PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>(PlugboardGraph::ConvertValue(PlugboardGraph::Eval(ss, i.connected_v, &info), PlugboardGraph::T_Float, good))};
|
||||
// }, &info, info.samples);
|
||||
static float xs1[100], ys1[100];
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
xs1[i] = i * 0.01f;
|
||||
ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX);
|
||||
ImPlot::SetupAxis(ImAxis_X1, "time", ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines);
|
||||
ImPlot::SetupAxis(ImAxis_Y1, "val", ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_Opposite | ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_AutoFit);
|
||||
|
||||
const f32 begin = view_left * static_cast<f32>(s.frame_max + 1), end = view_right * static_cast<f32>(s.frame_max + 1);
|
||||
ImPlot::SetupAxisLimits(ImAxis_X1, begin, end, ImGuiCond_Always);
|
||||
ImPlot::SetupFinish();
|
||||
|
||||
static bool started_dragging = false, dragging = false;
|
||||
if (started_dragging) {
|
||||
dragging = true;
|
||||
}
|
||||
static float xs2[50], ys2[50];
|
||||
for (int i = 0; i < 50; i++) {
|
||||
xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX);
|
||||
ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX);
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||
dragging = false;
|
||||
}
|
||||
u32 keys = 0;
|
||||
for (const auto& n : selected_chains) {
|
||||
PlugboardGraph::ConnectInfo i {n, 0}; // it's a chain -- 0 is out
|
||||
auto& [chain, chain_copy] = *std::any_cast<PlugboardNodes::ChainExtra>(&n->extra);
|
||||
|
||||
if (started_dragging)
|
||||
chain_copy = chain;
|
||||
|
||||
auto key_loop_target = &chain;
|
||||
if (dragging) {
|
||||
chain.segments.clear();
|
||||
chain.selected.clear();
|
||||
key_loop_target = &chain_copy;
|
||||
}
|
||||
|
||||
ImPlot::PlotScatter("Data 1", xs1, ys1, 100);
|
||||
ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f);
|
||||
ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1));
|
||||
ImPlot::PlotScatter("Data 2", xs2, ys2, 50);
|
||||
ImPlot::PopStyleVar();
|
||||
for (u32 ii = 0; ii < key_loop_target->segments.size(); ii++) {
|
||||
auto& [k, val, segment] = (key_loop_target->segments)[ii];
|
||||
ImGui::PushID(keys++);
|
||||
|
||||
auto sel_it = std::find(key_loop_target->selected.begin(), key_loop_target->selected.end(), ii);
|
||||
bool is_sel = sel_it != key_loop_target->selected.end();
|
||||
bool is_nxt_sel = ii + 1 < key_loop_target->segments.size() && std::find(key_loop_target->selected.begin(), key_loop_target->selected.end(), ii + 1) != key_loop_target->selected.end();
|
||||
|
||||
i32 frame = k;
|
||||
f64 v = val;
|
||||
i32 drag_off_x; // data coords
|
||||
f64 drag_off_y; // data coords
|
||||
ImPlotPoint mouse_pos = ImPlot::PixelsToPlot(io.MousePos),
|
||||
mouse_clicked_pos = ImPlot::PixelsToPlot(io.MouseClickedPos[ImGuiMouseButton_Left]);
|
||||
drag_off_x = static_cast<i32>(std::round(mouse_pos.x - mouse_clicked_pos.x));
|
||||
drag_off_y = mouse_pos.y - mouse_clicked_pos.y;
|
||||
if (dragging && is_sel) {
|
||||
frame += drag_off_x;
|
||||
v += drag_off_y;
|
||||
}
|
||||
|
||||
f64 x = frame, y = v;
|
||||
ImVec2 k_pos = ImPlot::PlotToPixels(x, y);
|
||||
|
||||
ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true);
|
||||
ImGui::SetCursorScreenPos(k_pos - ImVec2{10.0f / 2.0f, 10.0f / 2.0f});
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, is_sel ? 0xFFFFFF88 : 0xFFAA7777);
|
||||
ImGui::Button("##k", {10.0f, 10.0f});
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel,
|
||||
sel_it, segment, v, ii, started_dragging, k_pos);
|
||||
|
||||
if (segment.interp == PlugboardNodes::K_I_CubicBezier && ii + 1 < key_loop_target->segments.size()) {
|
||||
const auto& [k_nxt, val_nxt, _] = (key_loop_target->segments)[ii + 1];
|
||||
i32 frame_nxt = k_nxt;
|
||||
f64 v_nxt = val_nxt;
|
||||
if (dragging && is_nxt_sel) {
|
||||
frame_nxt += drag_off_x;
|
||||
v_nxt += drag_off_y;
|
||||
}
|
||||
|
||||
f64 p2x = std::lerp(frame, frame_nxt, segment.v[0]), p2y = std::lerp(v, v_nxt, segment.v[1]),
|
||||
p3x = std::lerp(frame, frame_nxt, segment.v[2]), p3y = std::lerp(v, v_nxt, segment.v[3]);
|
||||
|
||||
ImPlot::Annotation(p2x, p2y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true);
|
||||
ImPlot::Annotation(p3x, p3y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true);
|
||||
|
||||
static f32 x_tmp, y_tmp;
|
||||
|
||||
ImGui::SetCursorScreenPos(ImPlot::PlotToPixels(p2x, p2y) - ImVec2{10.0f / 2.0f, 10.0f / 2.0f});
|
||||
ImGui::Button("##p2", {10.0f, 10.0f});
|
||||
if (ImGui::IsItemClicked()) {
|
||||
x_tmp = segment.v[0];
|
||||
y_tmp = segment.v[1];
|
||||
}
|
||||
if (ImGui::IsItemActive()) {
|
||||
segment.v[0] = std::clamp(x_tmp + static_cast<f32>(drag_off_x) / (static_cast<f32>(frame_nxt) - static_cast<f32>(frame)), 0.0f, 1.0f);
|
||||
segment.v[1] = y_tmp + static_cast<f32>(drag_off_y / (v_nxt - v));
|
||||
}
|
||||
|
||||
ImGui::SetCursorScreenPos(ImPlot::PlotToPixels(p3x, p3y) - ImVec2{10.0f / 2.0f, 10.0f / 2.0f});
|
||||
ImGui::Button("##p3", {10.0f, 10.0f});
|
||||
if (ImGui::IsItemClicked()) {
|
||||
x_tmp = segment.v[2];
|
||||
y_tmp = segment.v[3];
|
||||
}
|
||||
if (ImGui::IsItemActive()) {
|
||||
segment.v[2] = std::clamp(x_tmp + static_cast<f32>(drag_off_x) / (static_cast<f32>(frame_nxt) - static_cast<f32>(frame)), 0.0f, 1.0f);
|
||||
segment.v[3] = y_tmp + static_cast<f32>(drag_off_y / (v_nxt - v));
|
||||
}
|
||||
|
||||
f64 xs[2] = {static_cast<f64>(frame), p2x},
|
||||
ys[2] = {v, p2y};
|
||||
|
||||
ImPlot::PlotLine("cb tangent", xs, ys, 2);
|
||||
xs[0] = p3x; xs[1] = static_cast<f64>(frame_nxt);
|
||||
ys[0] = p3y; ys[1] = v_nxt;
|
||||
ImPlot::PlotLine("cb tangent", xs, ys, 2);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
struct PlotInfo info{
|
||||
.begin = begin,
|
||||
.end = end,
|
||||
.samples = 100,
|
||||
.s = s,
|
||||
.connected_v = i
|
||||
};
|
||||
ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint {
|
||||
PlotInfo i = *(PlotInfo *) user_data;
|
||||
f32 x = i.begin + (i.end - i.begin) * static_cast<f32>(idx) / static_cast<f32>(i.samples);
|
||||
CompState ss = i.s;
|
||||
ss.current_frame = static_cast<u64>(x);
|
||||
bool good;
|
||||
Vector<PlugboardGraph::ConnectInfo> info;
|
||||
return {x, std::get<PlugboardGraph::T_Map<PlugboardGraph::T_Float>::type>(
|
||||
PlugboardGraph::ConvertValue(PlugboardGraph::Eval(ss, i.connected_v, &info),
|
||||
PlugboardGraph::T_Float, good))};
|
||||
}, &info, info.samples);
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
|
||||
if (started_dragging && dragging)
|
||||
started_dragging = false;
|
||||
}
|
||||
ImPlot::PopStyleColor(3);
|
||||
ImPlot::PopStyleVar(2);
|
||||
ImGui::SetCursorScreenPos(ImGui::GetCursorStartPos());
|
||||
draw_tl_bg_drag_area(ImGui::GetContentRegionAvail().y, ImGui::GetContentRegionAvail().x);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
|
@ -1298,6 +1427,24 @@ namespace K::UI {
|
|||
{fr, tl_init_pos.y + row_height},
|
||||
{fr, ImGui::GetWindowSize().y + tl_init_pos.y}, 0x773333FF, 1.0f);
|
||||
}
|
||||
if (bg_drag_select_active) {
|
||||
window->DrawList->AddRectFilled(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, 0x55AA5555);
|
||||
window->DrawList->AddRect(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, 0xAAAA7777);
|
||||
}
|
||||
if (window->Rect().Contains(io.MousePos) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && mouse_tl_x >= 0 && mouse_tl_x < view_width && io.MouseWheel != 0.0f) {
|
||||
f32 center = mouse_tl_x / view_width * view_amt + view_left;
|
||||
f32 new_view_amt = std::clamp(view_amt - 0.05f * io.MouseWheel, 0.05f, 1.0f);
|
||||
view_left = center - new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt;
|
||||
view_right = center + new_view_amt / 2.0f - (mouse_tl_x / view_width - .5f) * new_view_amt;
|
||||
if (view_left < 0.0f) {
|
||||
view_right -= view_left;
|
||||
view_left = 0.0f;
|
||||
}
|
||||
else if (view_right > 1.0f) {
|
||||
view_left -= (view_right - 1.0f);
|
||||
view_right = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
|
@ -1517,10 +1664,24 @@ namespace K::UI {
|
|||
ImPlot::SetupAxis(ImAxis_Y1, "val", ImPlotAxisFlags_Opposite | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_AutoFit);
|
||||
ImPlot::SetupFinish();
|
||||
constexpr static u32 samples = 100;
|
||||
|
||||
if (type_current == PlugboardNodes::K_I_CubicBezier) {
|
||||
f64 p2x = p2.x, p2y = p2.y, p3x = p3.x, p3y= p3.y;
|
||||
if (ImPlot::DragPoint(0, &p2x, &p2y, {1.0f, 1.0f, 1.0f, 1.0f})) {
|
||||
p2.x = static_cast<f32>(std::clamp(p2x, 0.0, 1.0));
|
||||
p2.y = static_cast<f32>(p2y);
|
||||
}
|
||||
if (ImPlot::DragPoint(1, &p3x, &p3y, {1.0f, 1.0f, 1.0f, 1.0f})) {
|
||||
p3.x = static_cast<f32>(std::clamp(p3x, 0.0, 1.0));
|
||||
p3.y = static_cast<f32>(p3y);
|
||||
}
|
||||
}
|
||||
|
||||
ImPlot::PlotLineG("v", [](i32 idx, void* user_data) {
|
||||
f32 t = static_cast<f32>(idx) / static_cast<f32>(samples);
|
||||
return ImPlotPoint{t, PlugboardNodes::EvalInterpolation(t, {.interp = type_current})};
|
||||
return ImPlotPoint{t, PlugboardNodes::EvalInterpolation(t, {.interp = type_current, .v = {p2.x,p2.y, p3.x, p3.y}})};
|
||||
}, nullptr, samples);
|
||||
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
ImPlot::PopStyleColor(3);
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace K::PlugboardNodes {
|
|||
|
||||
struct InterpolationExtra {
|
||||
K_Interpolation interp;
|
||||
f32 v[4];
|
||||
f32 v[4]; // currently used only for bezier tangent points -- between [0,1]^2 fitted to segment
|
||||
};
|
||||
|
||||
constexpr f32 EvalInterpolation(f32 x, const InterpolationExtra& extra) {
|
||||
|
|
4
TODO.md
4
TODO.md
|
@ -5,7 +5,6 @@
|
|||
- copy & paste
|
||||
- Undo's
|
||||
- Once this is done we can move the plugboard loop detection/chain finding out of the eval loop
|
||||
- Graph editor
|
||||
- Node groups
|
||||
- Simple export
|
||||
|
||||
|
@ -18,8 +17,7 @@
|
|||
- Data models
|
||||
- Dump and read back state, (de)serialization!!!
|
||||
- Pre-compose/Layer Groups (jokes -- can be completely UI side)
|
||||
- Resource
|
||||
- Deletion
|
||||
- Deleting layers/nodes/keys/etc upkeep
|
||||
- Motion blur
|
||||
- Text (idea: index-based evaluation in plugboard)
|
||||
- Shapes (idea: embed glisp :mmtroll:, need to inquire -- still a lot of friction for simple shapes if we don't also get the glisp gizmos)
|
||||
|
|
Loading…
Add table
Reference in a new issue