PeriDyno 1.2.1
Loading...
Searching...
No Matches
QtModuleFlowScene.cpp
Go to the documentation of this file.
1#include "QtModuleFlowScene.h"
2
3#include "Object.h"
4#include "SceneGraph.h"
5#include "QtModuleWidget.h"
6#include "SceneGraph.h"
7
8#include "nodes/QNode"
9
11#include "AutoLayoutDAG.h"
12#include "ModulePort.h"
13
14#include <QtWidgets/QMenu>
15#include <QLineEdit>
16#include <QtWidgets>
17
18namespace dyno
19{
20 class States : public Module
21 {
22 public:
23 States() {};
24
25 bool allowExported() override { return false; }
26 bool allowImported() override { return false; }
27
28 std::string caption() { return "States"; }
29 };
30
31 class Outputs : public Module
32 {
33 public:
34 Outputs() {};
35
36 bool allowExported() override { return false; }
37 bool allowImported() override { return false; }
38
39 std::string caption() { return "Outputs"; }
40 };
41}
42
43namespace Qt
44{
45 QPointF SimStatePos = QPointF(0.0f, 0.0f);
46 QPointF RenStatePos = QPointF(0.0f, 0.0f);
47
48 QtModuleFlowScene::QtModuleFlowScene(std::shared_ptr<QtDataModelRegistry> registry,
49 QObject* parent)
50 : QtFlowScene(registry, parent)
51 {
52 connect(this, &QtFlowScene::nodeMoved, this, &QtModuleFlowScene::moveModule);
53 }
54
56 : QtFlowScene(parent)
57 {
58 mNode = widget->getNode();
59
60 auto classMap = dyno::Object::getClassMap();
61 auto ret = std::make_shared<QtDataModelRegistry>();
62 int id = 0;
63 for (auto const c : *classMap)
64 {
65 id++;
66
67 QString str = QString::fromStdString(c.first);
68 dyno::Object* obj = dyno::Object::createObject(str.toStdString());
69 std::shared_ptr<dyno::Module> module(dynamic_cast<dyno::Module*>(obj));
70
71 if (module != nullptr)
72 {
73 QtDataModelRegistry::RegistryItemCreator creator = [str]() {
74 auto new_obj = dyno::Object::createObject(str.toStdString());
75 std::shared_ptr<dyno::Module> new_module(dynamic_cast<dyno::Module*>(new_obj));
76 auto dat = std::make_unique<QtModuleWidget>(new_module);
77 return dat;
78 };
79
80 QString category = QString::fromStdString(module->getModuleType());
81 ret->registerModel<QtModuleWidget>(category, creator);
82 }
83 }
84
85 this->setRegistry(ret);
86
87 if (mNode != nullptr)
88 showModuleFlow(mNode.get());
89
91
93
94 connect(this, &QtFlowScene::nodeMoved, this, &QtModuleFlowScene::moveModule);
95 connect(this, &QtFlowScene::nodePlaced, this, &QtModuleFlowScene::addModule);
96 connect(this, &QtFlowScene::nodeDeleted, this, &QtModuleFlowScene::deleteModule);
97
98 connect(this, &QtFlowScene::outPortConextMenu, this, &QtModuleFlowScene::promoteOutput);
99 }
100
102 {
103 //To avoid editing the the module flow inside the node when the widget is closed.
105 }
106
108 {
109 mEditingEnabled = true;
110
111 auto allNodes = this->allNodes();
112
113 for (auto node : allNodes)
114 {
115 auto model = dynamic_cast<QtModuleWidget*>(node->nodeDataModel());
116 if (model != nullptr)
117 {
118 model->enableEditing();
119 }
120 }
121 }
122
124 {
125 mEditingEnabled = false;
126
127 auto allNodes = this->allNodes();
128
129 for (auto node : allNodes)
130 {
131 auto model = dynamic_cast<QtModuleWidget*>(node->nodeDataModel());
132 if (model != nullptr)
133 {
134 model->disableEditing();
135 }
136 }
137 }
138
140 {
141 clearScene();
142
143 if (node == nullptr)
144 return;
145
146 auto& mlist = node->getModuleList();
147
148 std::map<dyno::ObjectId, QtNode*> moduleMap;
149
150 //To show the animation pipeline in default
151 if (mActivePipeline == nullptr)
153
154 auto& modules = mActivePipeline->allModules();
155
156 auto addModuleWidget = [&](std::shared_ptr<Module> m) -> void
157 {
158 auto mId = m->objectId();
159
160 auto type = std::make_unique<QtModuleWidget>(m);
161
162 auto& node = this->createNode(std::move(type));
163
164 moduleMap[mId] = &node;
165
166 QPointF posView(m->bx(), m->by());
167
168 node.nodeGraphicsObject().setPos(posView);
169
170 this->nodePlaced(node);
171 };
172
173 //Create a dummy module to store all state variables
174 mStates = std::make_shared<dyno::States>();
175
176 auto& fields = node->getAllFields();
177 for (auto field : fields)
178 {
179 if ((field->getFieldType() == dyno::FieldTypeEnum::State
180 || field->getFieldType() == dyno::FieldTypeEnum::In) && field->inputPolicy() == FBase::One)
181 {
182 mStates->addOutputField(field);
183 }
184 }
185
186
187 QPointF pos = mActivePipeline == node->animationPipeline() ? SimStatePos : RenStatePos;
188 mStates->setBlockCoord(pos.x(), pos.y());
189
190 addModuleWidget(mStates);
191
192 for (auto m : modules)
193 {
194 addModuleWidget(m.second);
195 }
196
197 auto createModuleConnections = [&](std::shared_ptr<Module> m) -> void
198 {
199 auto inId = m->objectId();
200
201 if (moduleMap.find(inId) != moduleMap.end()) {
202 auto inBlock = moduleMap[m->objectId()];
203
204 auto imports = m->getImportModules();
205
206 if (m->allowImported())
207 {
208 for (int i = 0; i < imports.size(); i++)
209 {
210 dyno::ModulePortType pType = imports[i]->getPortType();
211 if (dyno::Single == pType)
212 {
213 auto moduleSrc = imports[i]->getModules()[0];
214 if (moduleSrc != nullptr)
215 {
216 auto outId = moduleSrc->objectId();
217 if (moduleMap.find(outId) != moduleMap.end())
218 {
219 auto outBlock = moduleMap[moduleSrc->objectId()];
220 createConnection(*inBlock, i, *outBlock, 0);
221 }
222 }
223 }
224 else if (dyno::Multiple == pType)
225 {
226 //TODO: a weird problem exist here, if the expression "auto& nodes = ports[i]->getNodes()" is used,
227 //we still have to call clear to avoid memory leak.
228 auto& modules = imports[i]->getModules();
229 //ports[i]->clear();
230 for (int j = 0; j < modules.size(); j++)
231 {
232 if (modules[j] != nullptr)
233 {
234 auto outId = modules[j]->objectId();
235 if (moduleMap.find(outId) != moduleMap.end())
236 {
237 auto outBlock = moduleMap[outId];
238 createConnection(*inBlock, i, *outBlock, 0);
239 }
240 }
241 }
242 //nodes.clear();
243 }
244 }
245 }
246
247
248 auto fieldIn = m->getInputFields();
249
250 for (int i = 0; i < fieldIn.size(); i++)
251 {
252 auto fieldSrc = fieldIn[i]->getSource();
253 if (fieldSrc != nullptr) {
254 auto parSrc = fieldSrc->parent();
255 if (parSrc != nullptr)
256 {
257 Module* moduleSrc = dynamic_cast<Module*>(parSrc);
258 if (moduleSrc == nullptr)
259 {
260 moduleSrc = mStates.get();
261 }
262
263 auto outId = moduleSrc->objectId();
264 auto fieldsOut = moduleSrc->getOutputFields();
265
266 uint outFieldIndex = 0;
267 bool fieldFound = false;
268 for (auto f : fieldsOut)
269 {
270 if (f == fieldSrc)
271 {
272 fieldFound = true;
273 break;
274 }
275 outFieldIndex++;
276 }
277
278 if (moduleSrc->allowExported()) outFieldIndex++;
279
280 uint inFieldIndex = m->allowImported() ? i + imports.size() : i;
281
282 if (fieldFound && moduleMap.find(outId) != moduleMap.end())
283 {
284 auto outBlock = moduleMap[outId];
285 createConnection(*inBlock, inFieldIndex, *outBlock, outFieldIndex);
286 }
287 }
288 }
289 }
290 }
291 };
292
293 for (auto m : modules)
294 {
295 createModuleConnections(m.second);
296 }
297
298 //Create a module for outputs
299 mOutputs = std::make_shared<dyno::Outputs>();
300
301 auto hasModuleConnected = [&](FBase* f) -> bool
302 {
303 bool fieldFound = false;
304 auto fieldSrc = f->getSource();
305 if (fieldSrc != nullptr) {
306 auto parSrc = fieldSrc->parent();
307 if (parSrc != nullptr)
308 {
309 Module* moduleSrc = dynamic_cast<Module*>(parSrc);
310 if (moduleSrc == nullptr)
311 {
312 moduleSrc = mStates.get();
313 }
314
315 auto outId = moduleSrc->objectId();
316 auto fieldsOut = moduleSrc->getOutputFields();
317
318 if (moduleMap.find(outId) != moduleMap.end())
319 {
320 for (auto f : fieldsOut)
321 {
322 if (f == fieldSrc)
323 {
324 fieldFound = true;
325 break;
326 }
327 }
328 }
329 }
330 }
331 return fieldFound;
332 };
333
334 auto& output_fields = node->getOutputFields();
335 for (auto field : output_fields)
336 {
337 if (field->getFieldType() == dyno::FieldTypeEnum::Out && hasModuleConnected(field))
338 {
339 mOutputs->addInputField(field);
340 }
341 }
342
343 if (mOutputs->getInputFields().size() > 0)
344 {
345 mOutputs->setBlockCoord(pos.x() + 500, pos.y());
346 addModuleWidget(mOutputs);
347 createModuleConnections(mOutputs);
348 }
349 }
350
351 void QtModuleFlowScene::moveModule(QtNode& n, const QPointF& newLocation)
352 {
353 QtModuleWidget* mw = dynamic_cast<QtModuleWidget*>(n.nodeDataModel());
354
355 auto m = mw == nullptr ? nullptr : mw->getModule();
356
357 if (m != nullptr)
358 {
359 m->setBlockCoord(newLocation.x(), newLocation.y());
360 }
361 }
362
364 {
365 auto pipeline = mNode->resetPipeline();
366
367 if (mNode == nullptr || mActivePipeline == pipeline)
368 return;
369
370 mActivePipeline = pipeline;
371
373
376 mReorderResetPipeline = false;
377 }
378 }
379
381 {
382 auto pipeline = mNode->animationPipeline();
383
384 if (mNode == nullptr || mActivePipeline == pipeline)
385 return;
386
387 mActivePipeline = mNode->animationPipeline();
388
390 }
391
393 {
394 auto pipeline = mNode->graphicsPipeline();
395
396 if (mNode == nullptr || mActivePipeline == pipeline)
397 return;
398
399 mActivePipeline = mNode->graphicsPipeline();
400
402
406 }
407 }
408
410 {
411 mActivePipeline->forceUpdate();
412 }
413
414 void QtModuleFlowScene::promoteOutput(QtNode& n, const PortIndex index, const QPointF& pos)
415 {
416 //Index 0 is used for module connection
417 if (index == 0)
418 return;
419
420 QMenu portMenu;
421
422 portMenu.setObjectName("PortMenu");
423
424
425 auto exportAction = portMenu.addAction("Export");
426
427 connect(exportAction, &QAction::triggered, [&]()
428 {
429 auto dataModel = dynamic_cast<QtModuleWidget*>(n.nodeDataModel());
430
431 if (dataModel != nullptr)
432 {
433 auto m = dataModel->getModule();
434 if (m != nullptr)
435 {
436 auto fieldOut = m->getOutputFields();
437
438 mActivePipeline->promoteOutputToNode(fieldOut[index - 1]);
439 }
440
441 emit nodeExportChanged();
442 }
443 });
444
445 QPoint p(pos.x(), pos.y());
446
447 portMenu.exec(p);
448 }
449
451 {
453
454 clearScene();
455
456 if (mNode != nullptr)
457 showModuleFlow(mNode.get());
458
460 }
461
463 {
464 if (mActivePipeline == nullptr)
465 return;
466
468
469 auto constructDAG = [&](std::shared_ptr<Module> m) -> void
470 {
471 if (m->allowImported())
472 {
473 auto imports = m->getImportModules();
474 for (int i = 0; i < imports.size(); i++)
475 {
476 auto inId = m->objectId();
477 dyno::ModulePortType pType = imports[i]->getPortType();
478 if (dyno::Single == pType)
479 {
480 auto module = imports[i]->getModules()[0];
481 if (module != nullptr)
482 {
483 auto outId = module->objectId();
484
485 graph.addEdge(outId, inId);
486 }
487 }
488 else if (dyno::Multiple == pType)
489 {
490 auto& modules = imports[i]->getModules();
491 for (int j = 0; j < modules.size(); j++)
492 {
493 if (modules[j] != nullptr)
494 {
495 auto outId = modules[j]->objectId();
496
497 graph.addEdge(outId, inId);
498 }
499 }
500 //nodes.clear();
501 }
502 }
503 }
504
505 auto outId = m->objectId();
506 auto fieldOut = m->getOutputFields();
507 for (int i = 0; i < fieldOut.size(); i++)
508 {
509 auto& sinks = fieldOut[i]->getSinks();
510 for (auto sink : sinks)
511 {
512 if (sink != nullptr) {
513 auto parSrc = sink->parent();
514 if (parSrc != nullptr)
515 {
516 Module* nodeSrc = dynamic_cast<Module*>(parSrc);
517
518 if (nodeSrc != nullptr)
519 {
520 auto inId = nodeSrc->objectId();
521 graph.addEdge(outId, inId);
522 }
523 }
524 }
525 }
526 }
527 };
528
529 constructDAG(mStates);
530
531 auto& mlists = mActivePipeline->activeModules();
532 for (auto it = mlists.begin(); it != mlists.end(); it++)
533 {
534 constructDAG(*it);
535 }
536
537 dyno::AutoLayoutDAG layout(&graph);
538 layout.update();
539
540 //Set up the mapping from ObjectId to QtNode
541 auto& _nodes = this->nodes();
542 std::map<dyno::ObjectId, QtNode*> qtNodeMapper;
543 std::map<dyno::ObjectId, Module*> moduleMapper;
544 for (auto const& _node : _nodes)
545 {
546 auto const& qtNode = _node.second;
547 auto model = qtNode->nodeDataModel();
548
549 auto nodeData = dynamic_cast<QtModuleWidget*>(model);
550
551 if (model != nullptr)
552 {
553 auto m = nodeData->getModule();
554 if (m != nullptr)
555 {
556 qtNodeMapper[m->objectId()] = qtNode.get();
557 moduleMapper[m->objectId()] = m.get();
558 }
559 }
560 }
561
562 float offsetX = 0.0f;
563 for (size_t l = 0; l < layout.layerNumber(); l++)
564 {
565 auto& xc = layout.layer(l);
566
567 float offsetY = 0.0f;
568 float xMax = 0.0f;
569 for (size_t index = 0; index < xc.size(); index++)
570 {
571 dyno::ObjectId id = xc[index];
572 if (qtNodeMapper.find(id) != qtNodeMapper.end())
573 {
574 QtNode* qtNode = qtNodeMapper[id];
575 NodeGeometry& geo = qtNode->nodeGeometry();
576
577 float w = geo.width();
578 float h = geo.height();
579
580 xMax = std::max(xMax, w);
581
582 Module* node = moduleMapper[id];
583
584 node->setBlockCoord(offsetX, offsetY);
585
586 offsetY += (h + mDy);
587 }
588 }
589
590 offsetX += (xMax + mDx);
591 }
592
593 if (mActivePipeline == mNode->animationPipeline()) {
594 SimStatePos = QPointF(mStates->bx(), mStates->by());
595 }
596 else {
597 RenStatePos = QPointF(mStates->bx(), mStates->by());
598 }
599
600
601 qtNodeMapper.clear();
602 moduleMapper.clear();
603
605 }
606
608 {
609 if (mActivePipeline == nullptr)
610 return;
611
612 auto nodeData = dynamic_cast<QtModuleWidget*>(n.nodeDataModel());
613
614 if (mEditingEnabled && nodeData != nullptr) {
615 mActivePipeline->pushModule(nodeData->getModule());
616 }
617 }
618
620 {
621 if (mActivePipeline == nullptr)
622 return;
623
624 auto nodeData = dynamic_cast<QtModuleWidget*>(n.nodeDataModel());
625
626 if (mEditingEnabled && nodeData != nullptr) {
627 mActivePipeline->popModule(nodeData->getModule());
628
629 emit this->nodeDeselected();
630 }
631 }
632}
unsigned int uint
Definition VkReduce.h:5
int PortIndex
@ One
Definition FBase.h:56
std::shared_ptr< dyno::Module > mStates
std::shared_ptr< dyno::Pipeline > mActivePipeline
QtModuleFlowScene(std::shared_ptr< QtDataModelRegistry > registry, QObject *parent=Q_NULLPTR)
std::shared_ptr< dyno::Node > mNode
std::shared_ptr< dyno::Module > mOutputs
void moveModule(QtNode &n, const QPointF &newLocation)
void showModuleFlow(Node *node)
void promoteOutput(QtNode &n, const PortIndex index, const QPointF &pos)
std::shared_ptr< Module > getModule()
The model dictates the number of inputs and outputs for the Node.
std::shared_ptr< Node > getNode()
Automatic layout for directed acyclic graph Refer to "Sugiyama Algorithm" by Nikola S....
std::vector< ObjectId > & layer(size_t l)
Graph class represents a directed graph.
void addEdge(ObjectId v, ObjectId w)
virtual bool allowExported()
Definition Module.h:89
Module(std::string name="default")
Definition Module.cpp:9
std::list< std::shared_ptr< Module > > & getModuleList()
Definition Node.h:149
std::shared_ptr< AnimationPipeline > animationPipeline()
Definition Node.cpp:302
std::vector< FBase * > & getAllFields()
Definition OBase.cpp:202
std::vector< FBase * > & getOutputFields()
Definition OBase.h:179
void setBlockCoord(float x, float y)
Definition OBase.h:161
static std::map< std::string, ClassInfo * > * getClassMap()
Definition Object.cpp:42
static Object * createObject(std::string name)
Definition Object.cpp:33
ObjectId objectId()
Definition Object.h:129
bool allowImported() override
std::string caption()
Return the caption.
bool allowExported() override
bool allowExported() override
bool allowImported() override
std::string caption()
Return the caption.
QPointF RenStatePos
QPointF SimStatePos
This is an implementation of AdditiveCCD based on peridyno.
Definition Array.h:25
uint32_t ObjectId
Definition Object.h:110
ModulePortType
Definition ModulePort.h:27
@ In
Definition FBase.h:31
@ Out
Definition FBase.h:32
@ State
Definition FBase.h:35
@ Multiple
Definition NodePort.h:29
@ Single
Definition NodePort.h:28