Fix #113014: Improved sorting to keep node group outputs above inputs
The position validation when manipulating node group items now includes outputs..inputs order in addition to sockets..panels order. The method for finding a valid position has been simplified, it's just a single iteration of insertion sort. Versioning has been added to ensure files from 4.0 alpha with potentially unsorted sockets get re-sorted. This uses `std::stable_sort` so that sockets keep their relative order apart from the input/output grouping. Pull Request: https://projects.blender.org/blender/blender/pulls/113060
This commit is contained in:
parent
19ac7c0f87
commit
71732a9600
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 32
|
||||
#define BLENDER_FILE_SUBVERSION 33
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -780,40 +780,47 @@ int bNodeTreeInterfacePanel::find_valid_insert_position_for_item(
|
|||
NODE_INTERFACE_PANEL_ALLOW_SOCKETS_AFTER_PANELS);
|
||||
const blender::Span<const bNodeTreeInterfaceItem *> items = this->items();
|
||||
|
||||
int pos = initial_pos;
|
||||
|
||||
if (sockets_above_panels) {
|
||||
if (item.item_type == NODE_INTERFACE_PANEL) {
|
||||
/* Find the closest valid position from the end, only panels at or after #position. */
|
||||
for (int test_pos = items.size() - 1; test_pos >= initial_pos; test_pos--) {
|
||||
if (test_pos < 0) {
|
||||
/* Initial position is out of range but valid. */
|
||||
break;
|
||||
}
|
||||
if (items[test_pos]->item_type != NODE_INTERFACE_PANEL) {
|
||||
/* Found valid position, insert after the last socket item. */
|
||||
pos = test_pos + 1;
|
||||
break;
|
||||
}
|
||||
/* True if item a should be above item b. */
|
||||
auto item_compare = [sockets_above_panels](const bNodeTreeInterfaceItem &a,
|
||||
const bNodeTreeInterfaceItem &b) -> bool {
|
||||
if (a.item_type != b.item_type) {
|
||||
/* Keep sockets above panels. */
|
||||
if (sockets_above_panels) {
|
||||
return a.item_type == NODE_INTERFACE_SOCKET;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Find the closest valid position from the start, no panels at or after #position. */
|
||||
for (int test_pos = 0; test_pos <= initial_pos; test_pos++) {
|
||||
if (test_pos >= items.size()) {
|
||||
/* Initial position is out of range but valid. */
|
||||
break;
|
||||
}
|
||||
if (items[test_pos]->item_type == NODE_INTERFACE_PANEL) {
|
||||
/* Found valid position, inserting moves the first panel. */
|
||||
pos = test_pos;
|
||||
break;
|
||||
/* Keep outputs above inputs. */
|
||||
if (a.item_type == NODE_INTERFACE_SOCKET) {
|
||||
const bNodeTreeInterfaceSocket &sa = reinterpret_cast<const bNodeTreeInterfaceSocket &>(a);
|
||||
const bNodeTreeInterfaceSocket &sb = reinterpret_cast<const bNodeTreeInterfaceSocket &>(b);
|
||||
const bool is_output_a = sa.flag & NODE_INTERFACE_SOCKET_OUTPUT;
|
||||
const bool is_output_b = sb.flag & NODE_INTERFACE_SOCKET_OUTPUT;
|
||||
if (is_output_a != is_output_b) {
|
||||
return is_output_a;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (items.is_empty()) {
|
||||
return initial_pos;
|
||||
}
|
||||
|
||||
return pos;
|
||||
/* Insertion sort for a single item.
|
||||
* items.size() is a valid position for appending. */
|
||||
int test_pos = clamp_i(initial_pos, 0, items.size());
|
||||
/* Move upward until valid position found. */
|
||||
while (test_pos > 0 && item_compare(item, *items[test_pos - 1])) {
|
||||
--test_pos;
|
||||
}
|
||||
/* Move downward until valid position found.
|
||||
* Result can be out of range, this is valid, items get appended. */
|
||||
while (test_pos < items.size() && item_compare(*items[test_pos], item)) {
|
||||
++test_pos;
|
||||
}
|
||||
return test_pos;
|
||||
}
|
||||
|
||||
void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#define DNA_DEPRECATED_ALLOW
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
@ -984,6 +985,42 @@ static void version_node_group_split_socket(bNodeTreeInterface &tree_interface,
|
|||
csocket->flag &= ~NODE_INTERFACE_SOCKET_OUTPUT;
|
||||
}
|
||||
|
||||
static void versioning_node_group_sort_sockets_recursive(bNodeTreeInterfacePanel &panel)
|
||||
{
|
||||
/* True if item a should be above item b. */
|
||||
auto item_compare = [](const bNodeTreeInterfaceItem *a,
|
||||
const bNodeTreeInterfaceItem *b) -> bool {
|
||||
if (a->item_type != b->item_type) {
|
||||
/* Keep sockets above panels. */
|
||||
return a->item_type == NODE_INTERFACE_SOCKET;
|
||||
}
|
||||
else {
|
||||
/* Keep outputs above inputs. */
|
||||
if (a->item_type == NODE_INTERFACE_SOCKET) {
|
||||
const bNodeTreeInterfaceSocket *sa = reinterpret_cast<const bNodeTreeInterfaceSocket *>(a);
|
||||
const bNodeTreeInterfaceSocket *sb = reinterpret_cast<const bNodeTreeInterfaceSocket *>(b);
|
||||
const bool is_output_a = sa->flag & NODE_INTERFACE_SOCKET_OUTPUT;
|
||||
const bool is_output_b = sb->flag & NODE_INTERFACE_SOCKET_OUTPUT;
|
||||
if (is_output_a != is_output_b) {
|
||||
return is_output_a;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/* Sort panel content. */
|
||||
std::stable_sort(panel.items().begin(), panel.items().end(), item_compare);
|
||||
|
||||
/* Sort any child panels too. */
|
||||
for (bNodeTreeInterfaceItem *item : panel.items()) {
|
||||
if (item->item_type == NODE_INTERFACE_PANEL) {
|
||||
versioning_node_group_sort_sockets_recursive(
|
||||
*reinterpret_cast<bNodeTreeInterfacePanel *>(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void enable_geometry_nodes_is_modifier(Main &bmain)
|
||||
{
|
||||
/* Any node group with a first socket geometry output can potentially be a modifier. Previously
|
||||
|
@ -1640,6 +1677,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 33)) {
|
||||
/* Fix node group socket order by sorting outputs and inputs. */
|
||||
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
|
||||
versioning_node_group_sort_sockets_recursive(ntree->tree_interface.root_panel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue