Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GraphModel.cpp
1#include <Dataflow/QtGui/GraphEditor/GraphModel.hpp>
2
3#include <Dataflow/QtGui/GraphEditor/WidgetFactory.hpp>
4#include <Gui/ParameterSetEditor/ParameterSetEditor.hpp>
5#include <QPushButton>
6
7namespace Ra {
8namespace Dataflow {
9namespace QtGui {
10namespace GraphEditor {
11using namespace Ra::Dataflow::Core;
12
13GraphModel::GraphModel( std::shared_ptr<Core::DataflowGraph> graph ) :
14 m_graph { graph }, m_next_node_id { 0 } {
15 fill_factory_map();
16 sync_data();
17}
18
19GraphModel::~GraphModel() {
20 //
21}
22
23void GraphModel::fill_factory_map() {
24 auto factories = NodeFactoriesManager::factory_manager();
25 for ( const auto& [factoryName, factory] : factories ) {
26 for ( const auto& [model_name, creator] : factory->factory_map() ) {
27 auto f = creator.first;
28 m_model_name_to_factory[model_name] = f;
29 }
30 }
31}
32
33std::unordered_set<GraphModel::NodeId> GraphModel::allNodeIds() const {
34 return m_node_ids;
35}
36
38GraphModel::allConnectionIds( NodeId const nodeId ) const {
40
41 std::copy_if( m_connectivity.begin(),
42 m_connectivity.end(),
43 std::inserter( result, std::end( result ) ),
44 [&nodeId]( ConnectionId const& cid ) {
45 return cid.inNodeId == nodeId || cid.outNodeId == nodeId;
46 } );
47
48 return result;
49}
50
52GraphModel::connections( NodeId nodeId, PortType portType, PortIndex portIndex ) const {
54
55 std::copy_if( m_connectivity.begin(),
56 m_connectivity.end(),
57 std::inserter( result, std::end( result ) ),
58 [&portType, &portIndex, &nodeId]( ConnectionId const& cid ) {
59 return ( getNodeId( portType, cid ) == nodeId &&
60 getPortIndex( portType, cid ) == portIndex );
61 } );
62
63 return result;
64}
65
66bool GraphModel::connectionExists( ConnectionId const connectionId ) const {
67 return ( m_connectivity.find( connectionId ) != m_connectivity.end() );
68}
69
70void GraphModel::addInputOutputNodesForGraph() {
71 m_graph->add_input_output_nodes();
72 sync_data();
73}
74
75GraphModel::NodeId GraphModel::addNode( QString const nodeType ) {
76 if ( nodeType.toStdString() == GraphInputNode::node_typename() ||
77 nodeType.toStdString() == GraphOutputNode::node_typename() ) {
78 m_graph->add_input_output_nodes();
79 sync_data();
80 auto itr = std::find_if( m_node_id_to_ptr.begin(),
81 m_node_id_to_ptr.end(),
82 [node_typename = nodeType.toStdString()]( const auto& n ) {
83 return n.second->model_name() == node_typename;
84 } );
85 return itr->first;
86 }
87 auto f = m_model_name_to_factory.at( nodeType.toStdString() );
88 auto n = f( {} );
89 m_graph->add_node( n );
90
91 NodeId newId = newNodeId();
92 m_node_ids.insert( newId );
93 m_node_id_to_ptr[newId] = n;
94
95 Q_EMIT nodeCreated( newId );
96
97 return newId;
98}
99
100bool GraphModel::connectionPossible( ConnectionId const connectionId ) const {
101 auto in_node_id = connectionId.inNodeId;
102 auto out_node_id = connectionId.outNodeId;
103
104 auto in_port_id = connectionId.inPortIndex;
105 auto out_port_id = connectionId.outPortIndex;
106
107 bool ret = m_connectivity.find( connectionId ) == m_connectivity.end() &&
108 m_graph->can_link( m_node_id_to_ptr.at( out_node_id ).get(),
109 out_port_id,
110 m_node_id_to_ptr.at( in_node_id ).get(),
111 in_port_id );
112
113 return ret;
114}
115
116void GraphModel::addConnection( ConnectionId const connectionId ) {
117 m_connectivity.insert( connectionId );
118 auto in_node_id = connectionId.inNodeId;
119 auto out_node_id = connectionId.outNodeId;
120 auto in_port_id = connectionId.inPortIndex;
121 auto out_port_id = connectionId.outPortIndex;
122
123 m_graph->add_link( m_node_id_to_ptr.at( out_node_id ),
124 out_port_id,
125 m_node_id_to_ptr.at( in_node_id ),
126 in_port_id );
127
128 Q_EMIT connectionCreated( connectionId );
129 Q_EMIT nodeUpdated( in_node_id );
130 Q_EMIT nodeUpdated( out_node_id );
131}
132
133bool GraphModel::nodeExists( NodeId const nodeId ) const {
134 return ( m_node_ids.find( nodeId ) != m_node_ids.end() );
135}
136
137QWidget* GraphModel::getWidget( std::shared_ptr<Core::Node> node ) const {
138 QWidget* controlPanel = new QWidget;
139 controlPanel->setStyleSheet( "background-color:transparent;" );
140 QVBoxLayout* layout = new QVBoxLayout( controlPanel );
141
142 auto node_inputs = node->input_variables();
143 if ( node_inputs.size() > 0 ) {
144 auto controlPanelInputs = new Ra::Gui::VariableSetEditor( "Inputs default", nullptr );
145 controlPanelInputs->setShowUnspecified( true );
146
147 WidgetFactory ui_builder { node_inputs, controlPanelInputs, {} };
148 node_inputs.visit( ui_builder );
149 // empty controlPanel has 3 children, if no more, then no widget for the inputs
150 if ( controlPanelInputs->children().size() > 3 )
151 layout->addWidget( controlPanelInputs );
152 else
153 delete controlPanelInputs;
154 }
155 if ( node->parameters().size() > 0 ) {
156 auto controlPanelParams = new Ra::Gui::VariableSetEditor( "Parameters", nullptr );
157 controlPanelParams->setShowUnspecified( true );
158
159 WidgetFactory ui_builder { node->parameters(), controlPanelParams, {} };
160 node_inputs.visit( ui_builder );
161
162 layout->addWidget( controlPanelParams );
163 }
164 auto g = dynamic_cast<DataflowGraph*>( node.get() );
165 if ( g ) {
166 auto b = new QPushButton( "Edit" );
167 b->setFlat( true );
168 connect( b, &QPushButton::clicked, [this, node]() {
169 emit const_cast<GraphModel*>( this )->node_edited( node );
170 } );
171 layout->addWidget( b );
172 }
173 return controlPanel;
174}
175
176QVariant GraphModel::nodeData( NodeId nodeId, NodeRole role ) const {
177
178 QVariant result;
179 auto node_ptr = m_node_id_to_ptr.at( nodeId );
180
181 switch ( role ) {
182 case NodeRole::Type:
183 result = QString::fromStdString( node_ptr->model_name() );
184 break;
185
186 case NodeRole::Position:
187 result = m_node_geometry_data[nodeId].pos;
188 break;
189
190 case NodeRole::Size:
191 result = m_node_geometry_data[nodeId].size;
192 break;
193
194 case NodeRole::CaptionVisible:
195 result = true;
196 break;
197
198 case NodeRole::Caption:
199 result = QString::fromStdString( node_ptr->display_name() );
200 break;
201
202 case NodeRole::Style: {
203 auto style = StyleCollection::nodeStyle();
204 result = style.toJson().toVariantMap();
205 } break;
206
207 case NodeRole::InternalData:
208 break;
209
210 case NodeRole::InPortCount: {
211 unsigned int count = ( node_ptr->inputs().size() );
212 if ( node_ptr == m_graph->output_node() ) ++count;
213 result = ( count );
214 } break;
215
216 case NodeRole::OutPortCount: {
217 unsigned int count = ( node_ptr->outputs().size() );
218 if ( node_ptr == m_graph->input_node() ) ++count;
219 result = ( count );
220 } break;
221
222 case NodeRole::Widget:
223 if ( auto node_itr = m_node_widget.find( nodeId ); node_itr == m_node_widget.end() ) {
224 m_node_widget[nodeId] = getWidget( node_ptr );
225 }
226 result = QVariant::fromValue( m_node_widget[nodeId] );
227 break;
228 }
229
230 return result;
231}
232
233bool GraphModel::setNodeData( NodeId nodeId, NodeRole role, QVariant value ) {
234 bool result = false;
235 auto node_ptr = m_node_id_to_ptr.at( nodeId );
236
237 switch ( role ) {
238 case NodeRole::Type:
239 break;
240 case NodeRole::Position: {
241 auto pos = value.value<QPointF>();
242
243 m_node_geometry_data[nodeId].pos = pos;
244 nlohmann::json json = { { "position", { { "x", pos.x() }, { "y", pos.y() } } } };
245 node_ptr->add_metadata( json );
246 emit nodePositionUpdated( nodeId );
247
248 result = true;
249 } break;
250
251 case NodeRole::Size: {
252 m_node_geometry_data[nodeId].size = value.value<QSize>();
253 result = true;
254 } break;
255
256 case NodeRole::CaptionVisible:
257 break;
258
259 case NodeRole::Caption:
260 break;
261
262 case NodeRole::Style:
263 break;
264
265 case NodeRole::InternalData:
266 break;
267
268 case NodeRole::InPortCount:
269 break;
270
271 case NodeRole::OutPortCount:
272 break;
273
274 case NodeRole::Widget:
275 break;
276 }
277
278 return result;
279}
280
281QVariant
282GraphModel::portData( NodeId nodeId, PortType portType, PortIndex portIndex, PortRole role ) const {
283
284 auto n = m_node_id_to_ptr.at( nodeId );
285 if ( n == m_graph->input_node() || n == m_graph->output_node() ) {
286 switch ( role ) {
287 case PortRole::Data:
288 return QVariant();
289 break;
290
291 case PortRole::DataType: {
292 QString s;
293 if ( ( n == m_graph->input_node() && portIndex == n->inputs().size() ) ||
294 ( n == m_graph->output_node() && portIndex == n->inputs().size() ) )
295 s = QString( "any" );
296 else {
297 auto p = ( portType == PortType::In ) ? n->port_by_index( "in", portIndex )
298 : n->port_by_index( "out", portIndex );
299
300 s = QString::fromStdString( Ra::Core::Utils::simplifiedDemangledType( p->type() ) );
301 }
302 return QVariant::fromValue( QtNodes::NodeDataType { s, s } );
303 } break;
304
305 case PortRole::ConnectionPolicyRole:
306 if ( portType == PortType::In )
307 return QVariant::fromValue( ConnectionPolicy::One );
308 else
309 return QVariant::fromValue( ConnectionPolicy::Many );
310 break;
311
312 case PortRole::CaptionVisible:
313 return true;
314 break;
315
316 case PortRole::Caption: {
317 if ( n == m_graph->input_node() && portIndex == n->inputs().size() )
318 return QString( "new" );
319 if ( n == m_graph->output_node() && portIndex == n->inputs().size() )
320 return QString( "new" );
321
322 auto p = ( portType == PortType::In ) ? n->port_by_index( "in", portIndex )
323 : n->port_by_index( "out", portIndex );
324 return QString::fromStdString( p->name() );
325 } break;
326 }
327 return QVariant();
328 }
329
330 switch ( role ) {
331 case PortRole::Data:
332 return QVariant();
333 break;
334
335 case PortRole::DataType: {
336 auto p = ( portType == PortType::In ) ? n->port_by_index( "in", portIndex )
337 : n->port_by_index( "out", portIndex );
338 QString s = QString::fromStdString( Ra::Core::Utils::simplifiedDemangledType( p->type() ) );
339 return QVariant::fromValue( QtNodes::NodeDataType { s, s } );
340 } break;
341
342 case PortRole::ConnectionPolicyRole:
343 if ( portType == PortType::In )
344 return QVariant::fromValue( ConnectionPolicy::One );
345 else
346 return QVariant::fromValue( ConnectionPolicy::Many );
347 break;
348
349 case PortRole::CaptionVisible:
350 return true;
351 break;
352
353 case PortRole::Caption: {
354 auto p = ( portType == PortType::In ) ? n->port_by_index( "in", portIndex )
355 : n->port_by_index( "out", portIndex );
356 return QString::fromStdString( p->name() );
357 } break;
358 }
359
360 return QVariant();
361}
362
363bool GraphModel::setPortData( NodeId nodeId,
364 PortType portType,
365 PortIndex portIndex,
366 QVariant const& value,
367 PortRole role ) {
368 Q_UNUSED( nodeId );
369 Q_UNUSED( portType );
370 Q_UNUSED( portIndex );
371 Q_UNUSED( value );
372 Q_UNUSED( role );
373
374 return false;
375}
376
377bool GraphModel::deleteConnection( ConnectionId const connectionId ) {
378 bool disconnected = false;
379
380 auto it = m_connectivity.find( connectionId );
381
382 if ( it != m_connectivity.end() ) {
383 disconnected = true;
384 auto in_node_id = connectionId.inNodeId;
385 auto in_port_id = connectionId.inPortIndex;
386
387 m_graph->remove_link( m_node_id_to_ptr.at( in_node_id ), in_port_id );
388 m_connectivity.erase( it );
389 }
390
391 if ( disconnected ) emit connectionDeleted( connectionId );
392
393 return disconnected;
394}
395
396bool GraphModel::deleteNode( NodeId const nodeId ) {
397 // Delete connections to this node first.
398 auto connectionIds = allConnectionIds( nodeId );
399
400 for ( auto& cId : connectionIds ) {
401 deleteConnection( cId );
402 }
403
404 m_graph->remove_node( m_node_id_to_ptr.at( nodeId ) );
405 m_node_id_to_ptr.erase( nodeId );
406 m_node_widget.erase( nodeId );
407
408 m_node_ids.erase( nodeId );
409 m_node_geometry_data.erase( nodeId );
410
411 emit nodeDeleted( nodeId );
412
413 return true;
414}
415
416QJsonObject GraphModel::saveNode( NodeId const nodeId ) const {
417 QJsonObject nodeJson;
418
419 auto node = m_node_id_to_ptr.at( nodeId );
420
421 // get node's json
422 nlohmann::json json;
423 node->toJson( json );
424 {
425 // appens ui stuff
426 json["id"] = static_cast<qint64>( nodeId );
427 QPointF const pos = nodeData( nodeId, NodeRole::Position ).value<QPointF>();
428 json["position"] = { { "x", pos.x() }, { "y", pos.y() } };
429 }
430
431 // convert to QJsonObject
432 QJsonDocument jsonResponse = QJsonDocument::fromJson( json.dump().c_str() );
433 QJsonObject jsonObject = jsonResponse.object();
434 for ( auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); it++ ) {
435 nodeJson.insert( it.key(), it.value() );
436 }
437
438 return nodeJson;
439}
440
441void GraphModel::loadNode( QJsonObject const& nodeJson ) {
442
443 // init node from json
444 auto json = nlohmann::json::parse( QJsonDocument( nodeJson ).toJson() );
445 auto f = m_model_name_to_factory[json["model"]["name"]];
446 auto n = f( json );
447 m_graph->add_node( n );
448
449 // restore model and ui stuff
450 NodeId restoredNodeId = static_cast<NodeId>( nodeJson["id"].toInt() );
451
452 // Next NodeId must be larger that any id existing in the graph
453 m_next_node_id = std::max( m_next_node_id, restoredNodeId + 1 );
454
455 // Create new node.
456 m_node_ids.insert( restoredNodeId );
457
458 // Create new node.
459 m_node_ids.insert( restoredNodeId );
460 m_node_id_to_ptr[restoredNodeId] = n;
461
462 Q_EMIT nodeCreated( restoredNodeId );
463
464 {
465 QJsonObject posJson = nodeJson["position"].toObject();
466 QPointF const pos( posJson["x"].toDouble(), posJson["y"].toDouble() );
467
468 setNodeData( restoredNodeId, NodeRole::Position, pos );
469 }
470}
471
472void GraphModel::setGraph( std::shared_ptr<Core::DataflowGraph> graph ) {
473 m_graph = graph;
474 sync_data();
475}
476
477void GraphModel::sync_data() {
478 m_node_ids.clear();
479 m_node_id_to_ptr.clear();
480 m_connectivity.clear();
481 m_node_geometry_data.clear();
482 m_node_widget.clear();
483 m_next_node_id = 0;
484
485 // Create new nodes
486 for ( const auto& n : m_graph->nodes() ) {
487 NodeId newId = newNodeId();
488 m_node_ids.insert( newId );
489 m_node_id_to_ptr[newId] = n;
490 if ( auto position = n->metadata().find( "position" ); position != n->metadata().end() ) {
491 m_node_geometry_data[newId].pos.setX( position->at( "x" ) );
492 m_node_geometry_data[newId].pos.setY( position->at( "y" ) );
493 }
494 }
495
496 // from nodes input to output
497 for ( const auto& in_node : m_graph->nodes() ) {
498 // get in_node_id, skip m_graph input_node
499 auto in_node_itr = std::find_if(
500 m_node_id_to_ptr.begin(), m_node_id_to_ptr.end(), [in_node]( const auto& pair ) {
501 return pair.second.get() == in_node.get();
502 } );
503 if ( in_node_itr == m_node_id_to_ptr.end() ) {
504 LOG( Ra::Core::Utils::logERROR ) << "error graph structure in_node";
505 return;
506 }
507
508 // skip connection outside graph
509 if ( in_node_itr->second == m_graph->input_node() ) { continue; }
510
511 const auto& in_node_id = in_node_itr->first;
512
513 for ( size_t in_port_id = 0; in_port_id < in_node->inputs().size(); ++in_port_id ) {
514 const auto& in_port = in_node->inputs()[in_port_id];
515 const auto& out_port = in_port->link();
516
517 if ( out_port ) {
518 // get out node id
519 auto out_node = out_port->node();
520 if ( out_node ) {
521 // if linked to graph out_node, not from the model's graph, use
522 // out_node->graph as out_node
523 const auto graph_out_node = dynamic_cast<const GraphOutputNode*>( out_node );
524 if ( graph_out_node && graph_out_node->graph() != m_graph.get() ) {
525 out_node = graph_out_node->graph();
526 }
527
528 auto out_node_itr = std::find_if(
529 m_node_id_to_ptr.begin(),
530 m_node_id_to_ptr.end(),
531 [out_node]( const auto& pair ) { return pair.second.get() == out_node; } );
532 if ( out_node_itr == m_node_id_to_ptr.end() ) {
533 LOG( Ra::Core::Utils::logERROR )
534 << "error graph structure out_node, port " << out_port->name()
535 << " in node " << in_node->display_name() << " " << in_port->name();
536 return;
537 }
538
539 const auto& out_node_id = out_node_itr->first;
540
541 // get out port id
542 auto out_port_itr =
543 find_if( out_node->outputs().begin(),
544 out_node->outputs().end(),
545 [out_port]( auto p ) { return p.get() == out_port; } );
546 if ( out_port_itr == out_node->outputs().end() ) {
547 LOG( Ra::Core::Utils::logERROR )
548 << "error graph structure, out node " << out_node->display_name()
549 << " out_port, in node " << in_node->display_name() << " "
550 << in_port->name();
551 return;
552 }
553 const auto out_port_id =
554 std::distance( out_node->outputs().begin(), out_port_itr );
555
556 // set connection
557 ConnectionId connection_id;
558
559 connection_id.inNodeId = in_node_id;
560 connection_id.outNodeId = out_node_id;
561 connection_id.inPortIndex = in_port_id;
562 connection_id.outPortIndex = out_port_id;
563
564 m_connectivity.insert( connection_id );
565 }
566 }
567 }
568 }
569 emit modelReset();
570}
571
572void GraphModel::clear_node_widget( Core::Node* node ) {
573 auto node_itr =
574 std::find_if( m_node_id_to_ptr.begin(), m_node_id_to_ptr.end(), [node]( const auto& pair ) {
575 return pair.second.get() == node;
576 } );
577 if ( node_itr == m_node_id_to_ptr.end() ) {
578 LOG( Ra::Core::Utils::logERROR ) << "error try to clear widget of an unmanaged node";
579 return;
580 }
581 const auto& node_id = node_itr->first;
582 m_node_widget.erase( node_id );
583}
584
585} // namespace GraphEditor
586} // namespace QtGui
587} // namespace Dataflow
588} // namespace Ra
Represent a set of connected nodes that define a Direct Acyclic Computational Graph Ownership of node...
Base abstract class for all the nodes added and used by the node system.
Definition Node.hpp:40
Simple Widget for RenderParameter editing The editor will expose a control panel containing all of th...
T copy_if(T... args)
T count(T... args)
T distance(T... args)
T end(T... args)
T find_if(T... args)
T get(T... args)
T inserter(T... args)
T max(T... args)
auto factory(const NodeFactorySet::key_type &name) -> NodeFactorySet::mapped_type
Gets the given factory from the manager.
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:4