diff --git a/apps/shaders_lab/shaders_lab.exe b/apps/shaders_lab/shaders_lab.exe index fae62b12..76b9edb7 100755 Binary files a/apps/shaders_lab/shaders_lab.exe and b/apps/shaders_lab/shaders_lab.exe differ diff --git a/cpp/functions/gfx/dag_node_editor.cpp b/cpp/functions/gfx/dag_node_editor.cpp index 61c58586..3224b2ce 100644 --- a/cpp/functions/gfx/dag_node_editor.cpp +++ b/cpp/functions/gfx/dag_node_editor.cpp @@ -70,19 +70,19 @@ static ImVec4 kind_color(DagKind kind) { return ImVec4(1, 1, 1, 1); } -static constexpr float PIN_RADIUS = 7.0f; +static constexpr float PIN_RADIUS = 9.0f; static constexpr float PIN_DIAMETER = PIN_RADIUS * 2.0f; +static const ImVec4 PIN_COLOR = ImVec4(0.78f, 0.78f, 0.82f, 1.0f); +static const ImVec4 PIN_BORDER = ImVec4(0.20f, 0.20f, 0.22f, 1.0f); -// Draws a filled circle of `color` and reserves a 14x14 dummy item so the -// node editor can compute the pin's hit rect from the surrounding BeginPin. -// Adds an outline in a slightly darker tone for visibility. -static void draw_pin_circle(ImVec4 color) { +// Draws a filled circle and reserves a (PIN_DIAMETER × PIN_DIAMETER) item. +// All pins use the same neutral color since the data type is uniform (vec4). +static void draw_pin_circle() { ImVec2 cursor = ImGui::GetCursorScreenPos(); ImVec2 center = ImVec2(cursor.x + PIN_RADIUS, cursor.y + PIN_RADIUS); ImDrawList* dl = ImGui::GetWindowDrawList(); - dl->AddCircleFilled(center, PIN_RADIUS, ImGui::ColorConvertFloat4ToU32(color)); - ImVec4 border(color.x * 0.4f, color.y * 0.4f, color.z * 0.4f, 1.0f); - dl->AddCircle(center, PIN_RADIUS, ImGui::ColorConvertFloat4ToU32(border), 0, 1.5f); + dl->AddCircleFilled(center, PIN_RADIUS, ImGui::ColorConvertFloat4ToU32(PIN_COLOR)); + dl->AddCircle(center, PIN_RADIUS, ImGui::ColorConvertFloat4ToU32(PIN_BORDER), 0, 1.5f); ImGui::Dummy(ImVec2(PIN_DIAMETER, PIN_DIAMETER)); } @@ -233,19 +233,24 @@ bool dag_node_editor(std::vector& pipeline) { s_positioned.insert(step.editor_uid); } + // Zero lateral padding so the input/output pin circles sit flush + // with the node's left and right edges. + ed::PushStyleVar(ed::StyleVar_NodePadding, ImVec4(0, 8, 0, 8)); ed::BeginNode(ed::NodeId(node_id(step.editor_uid))); - // Header + // Header (with horizontal padding so the title doesn't touch the edge) + ImGui::Dummy(ImVec2(8, 0)); + ImGui::SameLine(0, 0); ImGui::PushStyleColor(ImGuiCol_Text, col); ImGui::TextUnformatted(def->label.c_str()); ImGui::PopStyleColor(); - ImGui::Dummy(ImVec2(0, 2)); + ImGui::Dummy(ImVec2(0, 4)); int ni = def->num_inputs; bool has_output_pin = (def->kind != DagKind::Output); // ── Three-column layout: inputs · controls · output ────────── - ImGui::BeginGroup(); // inputs column + ImGui::BeginGroup(); // inputs column (left edge) if (ni == 0) { ImGui::Dummy(ImVec2(PIN_DIAMETER, PIN_DIAMETER)); } @@ -253,14 +258,14 @@ bool dag_node_editor(std::vector& pipeline) { ed::BeginPin(ed::PinId(input_pin_id(step.editor_uid, k)), ed::PinKind::Input); ed::PinPivotAlignment(ImVec2(0.0f, 0.5f)); ed::PinPivotSize(ImVec2(0, 0)); - draw_pin_circle(col); + draw_pin_circle(); ed::EndPin(); } ImGui::EndGroup(); - ImGui::SameLine(); + ImGui::SameLine(0, 8); // gap between pin column and controls - ImGui::BeginGroup(); // controls column + ImGui::BeginGroup(); // controls column (centre, with internal padding) ImGui::PushID(static_cast(step.editor_uid)); if (def->controls.empty() && def->kind != DagKind::Output) { ImGui::Dummy(ImVec2(60, PIN_DIAMETER)); @@ -295,14 +300,14 @@ bool dag_node_editor(std::vector& pipeline) { ImGui::PopID(); ImGui::EndGroup(); - ImGui::SameLine(); + ImGui::SameLine(0, 8); // gap between controls and output pin - ImGui::BeginGroup(); // output column + ImGui::BeginGroup(); // output column (right edge) if (has_output_pin) { ed::BeginPin(ed::PinId(output_pin_id(step.editor_uid)), ed::PinKind::Output); ed::PinPivotAlignment(ImVec2(1.0f, 0.5f)); ed::PinPivotSize(ImVec2(0, 0)); - draw_pin_circle(col); + draw_pin_circle(); ed::EndPin(); } else { ImGui::Dummy(ImVec2(PIN_DIAMETER, PIN_DIAMETER)); @@ -310,6 +315,7 @@ bool dag_node_editor(std::vector& pipeline) { ImGui::EndGroup(); ed::EndNode(); + ed::PopStyleVar(); } // ── Draw existing links ────────────────────────────────────────────────── @@ -323,12 +329,10 @@ bool dag_node_editor(std::vector& pipeline) { int src_idx = find_by_id(pipeline, sid); if (src_idx < 0) continue; const DagStep& src_step = pipeline[static_cast(src_idx)]; - const DagNodeDef* src_def = dag_find(src_step.name); - ImVec4 link_col = src_def ? kind_color(src_def->kind) : ImVec4(0.7f, 0.7f, 0.7f, 1.0f); ed::Link(ed::LinkId(link_id(src_step.editor_uid, step.editor_uid, k)), ed::PinId(output_pin_id(src_step.editor_uid)), ed::PinId(input_pin_id(step.editor_uid, k)), - link_col, 2.5f); + PIN_COLOR, 2.5f); } } @@ -349,6 +353,37 @@ bool dag_node_editor(std::vector& pipeline) { ed::Resume(); } + // ── Right-click on a pin clears all connections of that pin ──────────── + { + ed::Suspend(); + ed::PinId ctx_pid; + if (ed::ShowPinContextMenu(&ctx_pid)) { + uintptr_t raw = ctx_pid.Get(); + uint32_t uid = uid_from_pin(raw); + if (is_output_pin(raw)) { + // Output pin: clear every source_ids entry pointing to this node + int idx = find_by_uid(pipeline, uid); + if (idx >= 0) { + const std::string source_id = pipeline[static_cast(idx)].id; + for (auto& s : pipeline) { + for (auto& sid : s.source_ids) { + if (sid == source_id) { sid.clear(); changed = true; } + } + } + } + } else { + // Input pin: clear that single slot + int slot = slot_from_input_pin(raw); + int idx = find_by_uid(pipeline, uid); + if (idx >= 0 && slot >= 0 && slot < 4) { + auto& sid = pipeline[static_cast(idx)].source_ids[static_cast(slot)]; + if (!sid.empty()) { sid.clear(); changed = true; } + } + } + } + ed::Resume(); + } + // ── Handle link creation ───────────────────────────────────────────────── if (ed::BeginCreate()) { ed::PinId start_pin, end_pin;