32 #include <QApplication>
33 #include <QDesktopWidget>
40 #include "../model/State.h"
41 #include "../model/Transition.h"
42 #include "../model/stateinstance/StateInstance.h"
47 #define INITIAL_TRANSITION_NAME ""
52 isConnectedToWorker(false), id(mediator_id), m_state(state)
54 dpi.setWidth(qApp->desktop()->logicalDpiX());
55 dpi.setHeight(qApp->desktop()->logicalDpiY());
59 initialTransition = NULL;
60 invisibleStartNode = NULL;
61 m_graph = std::make_shared<LockableGraph>();
79 if (!graph || !graph->graph)
92 isConnectedToWorker =
true;
117 emit scheduleMe(
id,
true);
158 if (!isConnectedToWorker)
163 bool layoutAll =
false;
170 ARMARX_INFO_S <<
"new substate " << substate->getInstanceName();
177 ARMARX_INFO_S <<
"removed substate " << substate->getInstanceName();
178 removeNode(substate);
191 updateNodeName(substate);
193 if (!updateNodePositionAndDimension(substate))
199 ARMARX_INFO_S <<
"state changed " << substate->getInstanceName();
208 emit scheduleMe(
id, layoutAll);
216 if (!isConnectedToWorker)
221 if (!transition->sourceState && transition->destinationState)
223 if (!updateInitialTransition(transition, signalType))
237 removeEdge(transition);
250 updateEdgeLabel(transition);
252 updateEdgeTargetAndSource(transition);
254 updateEdgePosition(transition);
262 emit scheduleMe(
id,
false);
270 layoutRepetition = counter;
271 emit layout(layoutAll);
278 const qreal left = GD_bb(graph).LL.x * (dpi.width() /
DotDefaultDPI);
279 const qreal top = (GD_bb(graph).UR.y - GD_bb(graph).LL.y) * (dpi.height() /
DotDefaultDPI);
280 const qreal right = GD_bb(graph).UR.x * (dpi.width() /
DotDefaultDPI);
281 const qreal bottom = (GD_bb(graph).UR.y - GD_bb(graph).UR.y) * (dpi.height() /
DotDefaultDPI);
282 return QRectF(left, top, right - left, bottom - top);
291 m_state->setSubstateAreaSize(QSizeF(graphVizBB.width() * (dpi.width() /
DotDefaultDPI),
293 broadcastSubstatePositions();
294 broadcastTransitionSupportPoints();
295 if (layoutRepetition > 0)
297 startLayouting(
false, layoutRepetition);
301 emit layoutingFinished();
314 disconnect(SLOT(startLayouting(
bool)));
318 emit layoutingFinished();
321 emit mediatorDeleted();
328 armarx::StateModelLayoutMediator::updateInitialTransition(
332 std::unique_lock lock(m_graph->mutex);
333 if (transition->sourceState)
337 if (!transition->destinationState)
342 auto addFakeStartNode = [&,
this]()
344 if (!invisibleStartNode)
346 invisibleStartNode = agnode(m_graph->graph,
const_cast<char*
>(
""), TRUE);
347 std::string posString = converter.convertFromPoint(QPointF(0, 0));
348 setGraphAttribute(invisibleStartNode,
"pos", posString +
"!");
349 setGraphAttribute(invisibleStartNode,
"shape",
"circle");
350 setGraphAttribute(invisibleStartNode,
"width",
"0.4");
351 setGraphAttribute(invisibleStartNode,
"height",
"0.4");
361 auto destinationNode = getNode(transition->destinationState);
362 if (!initialTransition && invisibleStartNode && destinationNode)
364 initialTransition = agedge(m_graph->graph,
375 if (invisibleStartNode)
377 agdelete(m_graph->graph, invisibleStartNode);
380 if (initialTransition)
382 agdelete(m_graph->graph, initialTransition);
385 initialTransition = NULL;
386 invisibleStartNode = NULL;
400 if (initialTransition)
402 agdelete(m_graph->graph, initialTransition);
403 initialTransition = NULL;
405 auto node = getNode(transition->destinationState);
411 <<
"could not find node for " << transition->destinationState->getInstanceName();
413 agedge(m_graph->graph, invisibleStartNode, node,
const_cast<char*
>(
"NULL"), TRUE);
423 armarx::StateModelLayoutMediator::convertPositionFromGraphViz(QPointF gvPos)
const
425 float fac = convertScaleFromGraphViz();
427 gvPos.setY(graphVizBB.height() -
430 gvPos += m_state->margin.topLeft();
435 armarx::StateModelLayoutMediator::convertScaleFromGraphViz()
const
438 const QSizeF& size = getState()->getSize();
440 float widthFac = graphVizBB.width() / (size.width() - m_state->margin.left());
441 float heightFac = graphVizBB.height() / (size.height() - m_state->margin.top());
442 float fac =
std::max(widthFac, heightFac);
447 armarx::StateModelLayoutMediator::convertPositionToGraphViz(QPointF statePos)
const
449 float fac = convertScaleFromGraphViz();
451 statePos -= m_state->margin.topLeft();
453 statePos.setY(graphVizBB.height() - statePos.y());
460 std::unique_lock lock(m_graph->mutex);
464 NodePtr node = getNode(substate);
470 if (!substate->getInstanceName().isEmpty())
472 name = substate->getInstanceName().toStdString();
474 else if (substate->getStateClass())
476 name = substate->getStateClass()->getStateName().toStdString();
480 name =
"NoStateClass";
483 agnode(m_graph->graph,
const_cast<char*
>(name.c_str()), TRUE);
485 std::pair<statechartmodel::StateInstancePtr, std::string>(substate, name));
486 updateNodePositionAndDimension(substate);
493 std::unique_lock lock(m_graph->mutex);
495 NodePtr node = getNode(substate);
496 if (!substate || !substate->getStateClass())
504 ARMARX_INFO_S <<
"Removing node " << substate->getInstanceName();
505 agdelete(m_graph->graph, node);
510 armarx::StateModelLayoutMediator::setNodeAttribute(
512 const std::string& attributeName,
513 const std::string& attributeValue)
515 std::unique_lock lock(m_graph->mutex);
517 NodePtr node = getNode(substate);
522 <<
" - Could not set attribute";
526 setGraphAttribute(node, attributeName, attributeValue);
530 armarx::StateModelLayoutMediator::getNodeAttribute(
532 const std::string& attributeName)
534 std::unique_lock lock(m_graph->mutex);
536 NodePtr node = getNode(substate);
540 return std::string();
543 std::string
value = getGraphAttribute(node, attributeName);
548 armarx::StateModelLayoutMediator::hasNodeAttribute(
550 const std::string& attributeName)
552 std::unique_lock lock(m_graph->mutex);
554 NodePtr node = getNode(substate);
561 return hasGraphAttribute(node, attributeName);
569 return std::string();
572 if (node->base.data->name)
574 return node->base.data->name;
583 armarx::StateModelLayoutMediator::getNode(
const std::string& name)
586 node = agnode(m_graph->graph,
const_cast<char*
>(name.c_str()), TRUE);
600 NodeMap::iterator iter = substateMap.find(substate);
602 if (iter != substateMap.end())
605 node = getNode(iter->second);
609 ARMARX_ERROR_S <<
"substateMap contains reference to non-exixtant node";
620 std::unique_lock lock(m_graph->mutex);
622 if (!transitionRepresentable(transition))
628 NodePtr destination = getNode(transition->destinationState);
629 std::string name = transition->eventName.toStdString();
631 if (destination &&
source)
635 agedge(m_graph->graph,
source, destination,
const_cast<char*
>(name.c_str()), TRUE);
636 setGraphAttribute(edge,
const_cast<char*
>(
"label"),
const_cast<char*
>(name.c_str()));
639 transitionList.append(transition);
643 ARMARX_ERROR_S <<
"The nodes requested for this transition don't exist in the graph";
650 std::unique_lock lock(m_graph->mutex);
652 EdgePtr edge = getEdge(transition);
655 transitionList.removeAll(transition);
660 agdelete(m_graph->graph, edge);
665 armarx::StateModelLayoutMediator::setEdgeAttribute(
667 std::string attributeName,
668 std::string attributeValue)
670 std::unique_lock lock(m_graph->mutex);
672 EdgePtr edge = getEdge(transition);
680 edge,
const_cast<char*
>(attributeName.c_str()),
const_cast<char*
>(attributeValue.c_str()));
684 armarx::StateModelLayoutMediator::getEdgeAttribute(
686 std::string attributeName)
688 std::unique_lock lock(m_graph->mutex);
690 EdgePtr edge = getEdge(transition);
694 return std::string();
697 std::string
value{getGraphAttribute(edge, attributeName)};
702 armarx::StateModelLayoutMediator::hasEdgeAttribute(
704 std::string attributeName)
706 std::unique_lock lock(m_graph->mutex);
708 EdgePtr edge = getEdge(transition);
715 return hasGraphAttribute(edge, attributeName);
724 if (transitionRepresentable(transition))
726 QString transName = transition->eventName;
729 if (!(transName.isEmpty()))
731 name = transName.toStdString();
734 NodePtr sourceNode = getNode(transition->sourceState);
735 NodePtr destNode = getNode(transition->destinationState);
740 for (
EdgePtr e = agfstedge(m_graph->graph, sourceNode); e;
741 e = agnxtedge(m_graph->graph, e, sourceNode))
743 std::string currentName{getGraphAttribute(e,
"label")};
746 if ((currentName == name) && (agtail(e) == sourceNode))
755 for (
EdgePtr e = agfstedge(m_graph->graph, destNode); e;
756 e = agnxtedge(m_graph->graph, e, destNode))
758 std::string currentName{getGraphAttribute(e,
"label")};
761 if ((currentName == name) && (aghead(e) == destNode))
774 armarx::StateModelLayoutMediator::updateNodePositionAndDimension(
778 bool changed =
false;
779 auto size = substate->getClassSize() * substate->getScale();
780 QPointF middlePosition = convertPositionToGraphViz(substate->getTopLeft() +
781 QPointF(size.width(), size.height()) * 0.5);
782 if (hasNodeAttribute(substate,
"pos"))
784 auto posString = getNodeAttribute(substate,
"pos");
785 if (posString.empty())
791 auto l = (converter.convertToPoint(posString) - middlePosition).manhattanLength();
801 auto posString = converter.convertFromPoint(middlePosition);
802 setNodeAttribute(substate,
"pos", posString +
"!");
803 setNodeAttribute(substate, (
char*)
"shape", (
char*)
"box");
810 size = substate->getClassSize() * substate->getScale();
821 if (hasNodeAttribute(substate,
"width"))
823 auto oldWidthString = getNodeAttribute(substate,
"width");
824 auto ow = size.width() / dpi.width();
826 oldWidthString.empty() || fabs(converter.convertToFloat(oldWidthString) - ow) > 0.01;
832 std::string widthString{converter.convertFromFloat(size.width() / dpi.width())};
833 setNodeAttribute(substate,
"width", widthString);
835 if (hasNodeAttribute(substate,
"height"))
837 auto oldheightString = getNodeAttribute(substate,
"height");
838 changed |= oldheightString.empty() || fabs(converter.convertToFloat(oldheightString) -
839 size.height() / dpi.height()) > 0.01;
846 std::string heightString{converter.convertFromFloat(size.height() / dpi.height())};
848 setNodeAttribute(substate,
"height", heightString);
850 setNodeAttribute(substate,
"pin",
"true");
859 if (!substate->getInstanceName().isEmpty())
861 newName = substate->getInstanceName().toStdString();
863 else if (substate->getStateClass())
865 newName = substate->getStateClass()->getStateName().toStdString();
869 newName =
"NoStateClass";
873 auto iter = substateMap.find(substate);
875 if (iter != substateMap.end())
877 oldName = iter->second;
879 if (newName != oldName)
881 std::unique_lock lock(m_graph->mutex);
883 NodePtr node = getNode(oldName);
884 setGraphAttribute(node,
"label", newName);
889 ARMARX_ERROR_S <<
"The substate " << newName <<
" is not in the graph";
895 armarx::StateModelLayoutMediator::updateEdgeLabel(
898 std::string oldLabel{getEdgeAttribute(transition,
"label")};
899 std::string newLabel{transition->eventName.toStdString()};
901 if ((!(oldLabel.empty())) && (oldLabel != newLabel))
903 setEdgeAttribute(transition,
"label", newLabel);
908 armarx::StateModelLayoutMediator::updateEdgeTargetAndSource(
911 std::unique_lock lock(m_graph->mutex);
913 if (transitionRepresentable(transition))
916 EdgePtr edge = getEdge(transition);
920 std::string oldSourceName = getNodeName(agtail(edge));
921 std::string oldDestinationName = getNodeName(aghead(edge));
923 if ((!oldSourceName.empty()) && (!oldDestinationName.empty()))
926 std::string newSourceName =
927 transition->sourceState->getInstanceName().toStdString();
928 std::string newDestName =
929 transition->destinationState->getInstanceName().toStdString();
931 if ((oldSourceName != newSourceName) || (oldDestinationName != newDestName))
934 removeEdge(transition);
951 removeEdge(transition);
956 armarx::StateModelLayoutMediator::updateEdgePosition(
961 if (transitionRepresentable(transition))
963 SupportPoints spline;
964 spline = transition->supportPoints;
965 std::string posString = converter.convertFromSpline(spline);
966 setEdgeAttribute(transition,
"pos", posString);
971 armarx::StateModelLayoutMediator::broadcastSubstatePositions()
975 for (
auto substate : substateMap)
979 std::string currentPosString = getNodeAttribute(substate.first,
"pos");
981 std::string widthString = getNodeAttribute(substate.first,
"width");
982 std::string heightString = getNodeAttribute(substate.first,
"height");
983 float width = converter.convertToFloat(widthString) * dpi.width();
984 float height = converter.convertToFloat(heightString) * dpi.height();
985 QPointF position = converter.convertToPoint(currentPosString);
986 position = convertPositionFromGraphViz(position);
989 instance->setBoundingBox(
std::max(width, height));
990 instance->setPosition(position - QPoint(width, height) * 0.5);
994 scheduleMe(getID(),
true);
1000 armarx::StateModelLayoutMediator::broadcastTransitionSupportPoints()
1002 auto tmpList = transitionList;
1003 if (initialTransition)
1005 const auto* state = m_state.get();
1009 tmpList.push_back(t);
1012 for (
auto& transition : tmpList)
1018 std::string currentSplineString = getEdgeAttribute(transition,
"pos");
1021 for (
auto& p : supportPoints.controlPoints)
1023 p = convertPositionFromGraphViz(p);
1026 if (supportPoints.endPoint)
1028 *supportPoints.endPoint = convertPositionFromGraphViz(*supportPoints.endPoint);
1031 if (supportPoints.startPoint)
1033 *supportPoints.startPoint = convertPositionFromGraphViz(*supportPoints.startPoint);
1036 if (hasEdgeAttribute(transition,
"lp"))
1038 std::string labelPosString = getEdgeAttribute(transition,
"lp");
1039 if (!labelPosString.empty())
1041 QPointF labelPosition = converter.convertToPoint(labelPosString);
1042 labelPosition = convertPositionFromGraphViz(labelPosition);
1043 labelCenter = std::make_shared<QPointF>(labelPosition);
1047 if (hasEdgeAttribute(transition,
"fontsize"))
1049 std::string labelFontPointSizeString = getEdgeAttribute(transition,
"fontsize");
1050 labelFontPointSize = std::make_shared<float>(
1051 converter.convertToFloat(labelFontPointSizeString) * 0.3);
1057 emit supportPointsChanged(
1058 transition, supportPoints.controlPointList(), labelCenter, labelFontPointSize);
1063 emit scheduleMe(getID(),
true);
1069 armarx::StateModelLayoutMediator::transitionRepresentable(
1072 return (transition->sourceState && transition->destinationState) ||
1073 (!transition->sourceState && transition->destinationState);