User:Barche/Node Selection Storage
From K-3D
Problem
- Currently, selected nodes are processed in the order they were created in the document. For certain modifiers that take multiple inputs, it would be helpful if they were processed in the order they were selected, instead.
- Node selection needs to remain "soft", i.e. store as a floating point value between 0 and 1.
- Ideally, node selection should no longer be stored in the nodes themselves, so the k3d::iselectable interface can be deprecated.
Proposed solution
Basically, we add a plugin "NodeSelection" to the document that implements the proposed k3d::iselected_nodes interface:
/// Abstract interface for objects that store document node selections and their weights
class inode_selection :
public virtual iunknown
{
public:
/// Storage for a list of selected nodes
/**
* Note: we use a list for fast removal at any position. Fast random access is not needed
*/
typedef std::list<inode*> selected_nodes_t;
/// Selects a node
/**
* \param Node the node to select
* \param Weight The selection weight. Setting this to 0 removes the node from the selection
*/
virtual void select(inode& Node, const double_t Weight) = 0;
/// Return the selection weight of the given node
virtual double_t selection_weight(inode& Node) = 0;
/// List of selected nodes, in the order they were selected
virtual const selected_nodes_t selected_nodes() = 0;
/// Deselect all nodes
virtual void deselect_all() = 0;
/// Changed signal emitted when the selection changed
typedef sigc::signal<void, ihint*> changed_signal_t;
virtual changed_signal_t& selection_changed_signal() = 0;
protected:
inode_selection() {}
inode_selection(const inode_selection&) {}
inode_selection& operator=(const inode_selection&) { return *this; }
virtual ~inode_selection() {}
};
Implementation details
- There is a prototype in modules/development which is benchmarked using tests/document.NodeSelection.benchmark.py
- Initial performance testing on a 1000 node document indicates selection weight lookup time is negligible (in the order of 1e-4s on an debug build)
- Selections are exposed through one property, which will need a custom control. Modifying this property externally also updates the ordered list of selected nodes, so the selection order is retained
- How do we find the SelectedNodes node quickly? This will be handled using a property to the render engine. For a global lookup, the idea at User:Barche/Special_Nodes could be used
Migration Status
Implementation on my machine is nearly done. Issues I encountered:
- I've added metadata to identify the node. To this end, a new method was added to nodes.h, to easily find nodes with given metadata:
template<typename interface_t>
const nodes_t find_nodes(inode_collection& Nodes, const string_t& MetaName, const string_t& MetaValue)
{
nodes_t meta_nodes = find_nodes<imetadata>(Nodes);
nodes_t nodes;
for(nodes_t::iterator node = meta_nodes.begin(); node != meta_nodes.end(); ++node)
{
imetadata* meta_node = dynamic_cast<imetadata*>(*node);
imetadata::metadata_t metadata = meta_node->get_metadata();
imetadata::metadata_t::iterator pair = metadata.find(MetaName);
if(pair != metadata.end() && pair->second == MetaValue)
{
if(dynamic_cast<interface_t*>(*node))
nodes.push_back(*node);
}
}
return nodes;
}
- k3dsdk/selection.h: select(inode*) and deselect(inode*) were removed. Rationale: avoid having a readily accessible method that could be called from anywhere and that results in the potentially expensive lookup of the inode_selection node
- Running the dashboard shows python.api.node.selection.py fails. I thought reimplementing the selection methods in node_python.cpp would help, but it still results in the error "RuntimeError: unknown property: selection_weight". Here is the changed implementation:
const double node::get_selection_weight() const
{
k3d::inode& node = *interface_wrapper<k3d::inode>::wrapped_ptr();
k3d::inode_collection::nodes_t nodes = k3d::find_nodes<k3d::inode_selection>(node.document().nodes(), "ngui:unique_node", "node_selection");
if(nodes.size() != 1)
throw std::runtime_error("internal error: node selection storage node not found");
k3d::inode_selection* node_selection = dynamic_cast<k3d::inode_selection*>(nodes.back());
return node_selection->selection_weight(node);
}
void node::set_selection_weight(const double Weight)
{
k3d::inode& node = *interface_wrapper<k3d::inode>::wrapped_ptr();
k3d::inode_collection::nodes_t nodes = k3d::find_nodes<k3d::inode_selection>(node.document().nodes(), "ngui:unique_node", "node_selection");
if(nodes.size() != 1)
throw std::runtime_error("internal error: node selection storage node not found");
k3d::inode_selection* node_selection = dynamic_cast<k3d::inode_selection*>(nodes.back());
node_selection->select(node, Weight);
}

