24 #include "../model/State.h"
25 #include "../model/Transition.h"
26 #include "../model/stateinstance/StateInstance.h"
40 #include <QApplication>
41 #include <QDesktopWidget>
44 #define INITIAL_TRANSITION_NAME ""
49 : isConnectedToWorker(false),
53 dpi.setWidth(qApp->desktop()->logicalDpiX());
54 dpi.setHeight(qApp->desktop()->logicalDpiY());
58 initialTransition = NULL;
59 invisibleStartNode = NULL;
60 m_graph = std::make_shared<LockableGraph>();
77 if (!graph || !graph->graph)
90 isConnectedToWorker =
true;
115 emit scheduleMe(
id,
true);
152 if (!isConnectedToWorker)
157 bool layoutAll =
false;
164 ARMARX_INFO_S <<
"new substate " << substate->getInstanceName();
171 ARMARX_INFO_S <<
"removed substate " << substate->getInstanceName();
172 removeNode(substate);
185 updateNodeName(substate);
187 if (!updateNodePositionAndDimension(substate))
193 ARMARX_INFO_S <<
"state changed " << substate->getInstanceName();
202 emit scheduleMe(
id, layoutAll);
207 if (!isConnectedToWorker)
212 if (!transition->sourceState && transition->destinationState)
214 if (!updateInitialTransition(transition, signalType))
228 removeEdge(transition);
241 updateEdgeLabel(transition);
243 updateEdgeTargetAndSource(transition);
245 updateEdgePosition(transition);
254 emit scheduleMe(
id,
false);
261 layoutRepetition = counter;
262 emit layout(layoutAll);
268 const qreal left = GD_bb(graph).LL.x * (dpi.width() /
DotDefaultDPI);
269 const qreal top = (GD_bb(graph).UR.y - GD_bb(graph).LL.y) * (dpi.height() /
DotDefaultDPI);
270 const qreal right = GD_bb(graph).UR.x * (dpi.width() /
DotDefaultDPI);
271 const qreal bottom = (GD_bb(graph).UR.y - GD_bb(graph).UR.y) * (dpi.height() /
DotDefaultDPI);
272 return QRectF(left, top, right - left, bottom - top);
280 m_state->setSubstateAreaSize(QSizeF(graphVizBB.width() * (dpi.width() /
DotDefaultDPI), graphVizBB.height() * (dpi.height() /
DotDefaultDPI)));
281 broadcastSubstatePositions();
282 broadcastTransitionSupportPoints();
283 if (layoutRepetition > 0)
285 startLayouting(
false, layoutRepetition);
289 emit layoutingFinished();
299 disconnect(SLOT(startLayouting(
bool)));
303 emit layoutingFinished();
306 emit mediatorDeleted();
314 std::unique_lock lock(m_graph->mutex);
315 if (transition->sourceState)
319 if (!transition->destinationState)
324 auto addFakeStartNode = [&,
this]()
326 if (!invisibleStartNode)
328 invisibleStartNode = agnode(m_graph->graph,
const_cast<char*
>(
""), TRUE);
329 std::string posString = converter.convertFromPoint(QPointF(0, 0));
330 setGraphAttribute(invisibleStartNode,
"pos", posString +
"!");
331 setGraphAttribute(invisibleStartNode,
"shape",
"circle");
332 setGraphAttribute(invisibleStartNode,
"width",
"0.4");
333 setGraphAttribute(invisibleStartNode,
"height",
"0.4");
343 auto destinationNode = getNode(transition->destinationState);
344 if (!initialTransition && invisibleStartNode && destinationNode)
346 initialTransition = agedge(m_graph->graph, invisibleStartNode, destinationNode,
const_cast<char*
>(
INITIAL_TRANSITION_NAME), TRUE);
353 if (invisibleStartNode)
355 agdelete(m_graph->graph, invisibleStartNode);
358 if (initialTransition)
360 agdelete(m_graph->graph, initialTransition);
363 initialTransition = NULL;
364 invisibleStartNode = NULL;
378 if (initialTransition)
380 agdelete(m_graph->graph, initialTransition);
381 initialTransition = NULL;
383 auto node = getNode(transition->destinationState);
388 ARMARX_CHECK_EXPRESSION(node) <<
"could not find node for " << transition->destinationState->getInstanceName();
389 initialTransition = agedge(m_graph->graph, invisibleStartNode, node,
const_cast<char*
>(
"NULL"), TRUE);
399 QPointF armarx::StateModelLayoutMediator::convertPositionFromGraphViz(QPointF gvPos)
const
401 float fac = convertScaleFromGraphViz();
403 gvPos.setY(graphVizBB.height() - gvPos.y());
405 gvPos += m_state->margin.topLeft();
409 float armarx::StateModelLayoutMediator::convertScaleFromGraphViz()
const
412 const QSizeF& size = getState()->getSize();
414 float widthFac = graphVizBB.width() / (size.width() - m_state->margin.left());
415 float heightFac = graphVizBB.height() / (size.height() - m_state->margin.top());
416 float fac =
std::max(widthFac, heightFac);
420 QPointF armarx::StateModelLayoutMediator::convertPositionToGraphViz(QPointF statePos)
const
422 float fac = convertScaleFromGraphViz();
424 statePos -= m_state->margin.topLeft();
426 statePos.setY(graphVizBB.height() - statePos.y());
432 std::unique_lock lock(m_graph->mutex);
436 NodePtr node = getNode(substate);
442 if (!substate->getInstanceName().isEmpty())
444 name = substate->getInstanceName().toStdString();
446 else if (substate->getStateClass())
448 name = substate->getStateClass()->getStateName().toStdString();
452 name =
"NoStateClass";
455 agnode(m_graph->graph,
const_cast<char*
>(name.c_str()), TRUE);
457 std::pair<statechartmodel::StateInstancePtr, std::string>(substate, name));
458 updateNodePositionAndDimension(substate);
464 std::unique_lock lock(m_graph->mutex);
466 NodePtr node = getNode(substate);
467 if (!substate || !substate->getStateClass())
475 ARMARX_INFO_S <<
"Removing node " << substate->getInstanceName();
476 agdelete(m_graph->graph, node);
481 const std::string& attributeName,
const std::string& attributeValue)
483 std::unique_lock lock(m_graph->mutex);
485 NodePtr node = getNode(substate);
489 ARMARX_WARNING_S <<
"Node null for state " << substate->getInstanceName() <<
" - Could not set attribute";
493 setGraphAttribute(node, attributeName, attributeValue);
498 std::unique_lock lock(m_graph->mutex);
500 NodePtr node = getNode(substate);
504 return std::string();
507 std::string
value = getGraphAttribute(node, attributeName);
512 std::unique_lock lock(m_graph->mutex);
514 NodePtr node = getNode(substate);
521 return hasGraphAttribute(node, attributeName);
524 std::string armarx::StateModelLayoutMediator::getNodeName(
armarx::NodePtr node)
528 return std::string();
531 if (node->base.data->name)
533 return node->base.data->name;
541 armarx::NodePtr armarx::StateModelLayoutMediator::getNode(
const std::string& name)
544 node = agnode(m_graph->graph,
const_cast<char*
>(name.c_str()), TRUE);
557 NodeMap::iterator iter = substateMap.find(substate);
559 if (iter != substateMap.end())
562 node = getNode(iter->second);
566 ARMARX_ERROR_S <<
"substateMap contains reference to non-exixtant node";
576 std::unique_lock lock(m_graph->mutex);
578 if (!transitionRepresentable(transition))
584 NodePtr destination = getNode(transition->destinationState);
585 std::string name = transition->eventName.toStdString();
587 if (destination &&
source)
590 EdgePtr edge = agedge(m_graph->graph,
source, destination,
const_cast<char*
>(name.c_str()), TRUE);
591 setGraphAttribute(edge,
const_cast<char*
>(
"label"),
const_cast<char*
>(name.c_str()));
594 transitionList.append(transition);
598 ARMARX_ERROR_S <<
"The nodes requested for this transition don't exist in the graph";
604 std::unique_lock lock(m_graph->mutex);
606 EdgePtr edge = getEdge(transition);
609 transitionList.removeAll(transition);
614 agdelete(m_graph->graph, edge);
620 std::unique_lock lock(m_graph->mutex);
622 EdgePtr edge = getEdge(transition);
629 agset(edge,
const_cast<char*
>(attributeName.c_str()),
const_cast<char*
>(attributeValue.c_str()));
634 std::unique_lock lock(m_graph->mutex);
636 EdgePtr edge = getEdge(transition);
640 return std::string();
643 std::string
value {getGraphAttribute(edge, attributeName)};
649 std::unique_lock lock(m_graph->mutex);
651 EdgePtr edge = getEdge(transition);
658 return hasGraphAttribute(edge, attributeName);
666 if (transitionRepresentable(transition))
668 QString transName = transition->eventName;
671 if (!(transName.isEmpty()))
673 name = transName.toStdString();
676 NodePtr sourceNode = getNode(transition->sourceState);
677 NodePtr destNode = getNode(transition->destinationState);
682 for (
EdgePtr e = agfstedge(m_graph->graph, sourceNode); e; e = agnxtedge(m_graph->graph, e, sourceNode))
684 std::string currentName {getGraphAttribute(e,
"label")};
687 if ((currentName == name) && (agtail(e) == sourceNode))
696 for (
EdgePtr e = agfstedge(m_graph->graph, destNode); e; e = agnxtedge(m_graph->graph, e, destNode))
698 std::string currentName {getGraphAttribute(e,
"label")};
701 if ((currentName == name) && (aghead(e) == destNode))
716 bool changed =
false;
717 auto size = substate->getClassSize() * substate->getScale();
718 QPointF middlePosition = convertPositionToGraphViz(substate->getTopLeft() + QPointF(size.width(), size.height()) * 0.5);
719 if (hasNodeAttribute(substate,
"pos"))
721 auto posString = getNodeAttribute(substate,
"pos");
722 if (posString.empty())
728 auto l = (converter.convertToPoint(posString) - middlePosition).manhattanLength();
738 auto posString = converter.convertFromPoint(middlePosition);
739 setNodeAttribute(substate,
"pos", posString +
"!");
740 setNodeAttribute(substate, (
char*)
"shape", (
char*)
"box");
747 size = substate->getClassSize() * substate->getScale();
758 if (hasNodeAttribute(substate,
"width"))
760 auto oldWidthString = getNodeAttribute(substate,
"width");
761 auto ow = size.width() / dpi.width();
762 changed |= oldWidthString.empty() || fabs(converter.convertToFloat(oldWidthString) - ow) > 0.01;
768 std::string widthString {converter.convertFromFloat(size.width() / dpi.width())};
769 setNodeAttribute(substate,
"width", widthString);
771 if (hasNodeAttribute(substate,
"height"))
773 auto oldheightString = getNodeAttribute(substate,
"height");
774 changed |= oldheightString.empty() || fabs(converter.convertToFloat(oldheightString) - size.height() / dpi.height()) > 0.01;
781 std::string heightString {converter.convertFromFloat(size.height() / dpi.height())};
783 setNodeAttribute(substate,
"height", heightString);
785 setNodeAttribute(substate,
"pin",
"true");
793 if (!substate->getInstanceName().isEmpty())
795 newName = substate->getInstanceName().toStdString();
797 else if (substate->getStateClass())
799 newName = substate->getStateClass()->getStateName().toStdString();
803 newName =
"NoStateClass";
807 auto iter = substateMap.find(substate);
809 if (iter != substateMap.end())
811 oldName = iter->second;
813 if (newName != oldName)
815 std::unique_lock lock(m_graph->mutex);
817 NodePtr node = getNode(oldName);
818 setGraphAttribute(node,
"label", newName);
823 ARMARX_ERROR_S <<
"The substate " << newName <<
" is not in the graph";
830 std::string oldLabel {getEdgeAttribute(transition,
"label")};
831 std::string newLabel {transition->eventName.toStdString()};
833 if ((!(oldLabel.empty())) && (oldLabel != newLabel))
835 setEdgeAttribute(transition,
"label", newLabel);
841 std::unique_lock lock(m_graph->mutex);
843 if (transitionRepresentable(transition))
846 EdgePtr edge = getEdge(transition);
850 std::string oldSourceName = getNodeName(agtail(edge));
851 std::string oldDestinationName = getNodeName(aghead(edge));
853 if ((!oldSourceName.empty()) && (!oldDestinationName.empty()))
856 std::string newSourceName = transition->sourceState->getInstanceName().toStdString();
857 std::string newDestName = transition->destinationState->getInstanceName().toStdString();
859 if ((oldSourceName != newSourceName) || (oldDestinationName != newDestName))
862 removeEdge(transition);
879 removeEdge(transition);
887 if (transitionRepresentable(transition))
889 SupportPoints spline;
890 spline = transition->supportPoints;
891 std::string posString = converter.convertFromSpline(spline);
892 setEdgeAttribute(transition,
"pos", posString);
896 void armarx::StateModelLayoutMediator::broadcastSubstatePositions()
900 for (
auto substate : substateMap)
904 std::string currentPosString = getNodeAttribute(substate.first,
"pos");
906 std::string widthString = getNodeAttribute(substate.first,
"width");
907 std::string heightString = getNodeAttribute(substate.first,
"height");
908 float width = converter.convertToFloat(widthString) * dpi.width();
909 float height = converter.convertToFloat(heightString) * dpi.height();
910 QPointF position = converter.convertToPoint(currentPosString);
911 position = convertPositionFromGraphViz(position);
914 instance->setBoundingBox(
std::max(width, height)
916 instance->setPosition(position - QPoint(width, height) * 0.5);
920 scheduleMe(getID(),
true);
925 void armarx::StateModelLayoutMediator::broadcastTransitionSupportPoints()
927 auto tmpList = transitionList;
928 if (initialTransition)
930 const auto* state = m_state.get();
934 tmpList.push_back(t);
937 for (
auto& transition : tmpList)
943 std::string currentSplineString = getEdgeAttribute(transition,
"pos");
946 for (
auto& p : supportPoints.controlPoints)
948 p = convertPositionFromGraphViz(p);
951 if (supportPoints.endPoint)
953 *supportPoints.endPoint = convertPositionFromGraphViz(*supportPoints.endPoint);
956 if (supportPoints.startPoint)
958 *supportPoints.startPoint = convertPositionFromGraphViz(*supportPoints.startPoint);
961 if (hasEdgeAttribute(transition,
"lp"))
963 std::string labelPosString = getEdgeAttribute(transition,
"lp");
964 if (!labelPosString.empty())
966 QPointF labelPosition = converter.convertToPoint(labelPosString);
967 labelPosition = convertPositionFromGraphViz(labelPosition);
968 labelCenter = std::make_shared<QPointF>(labelPosition);
972 if (hasEdgeAttribute(transition,
"fontsize"))
974 std::string labelFontPointSizeString = getEdgeAttribute(transition,
"fontsize");
975 labelFontPointSize = std::make_shared<float>(converter.convertToFloat(labelFontPointSizeString) * 0.3);
981 emit supportPointsChanged(transition, supportPoints.controlPointList(), labelCenter, labelFontPointSize);
986 emit scheduleMe(getID(),
true);
993 return (transition->sourceState && transition->destinationState) || (!transition->sourceState && transition->destinationState);