26 #include "../model/Transition.h"
27 #include "../model/stateinstance/StateInstance.h"
29 #include <QGraphicsSceneMouseEvent>
32 #include <QGraphicsScene>
39 #include <QGraphicsView>
40 #include <QPropertyAnimation>
41 #include <QStyleOptionGraphicsItem>
48 #define INITIAL_CIRCLE_RADIUS 10
54 QGraphicsPathItem(parent),
56 transition(transition),
57 transitionLabel(NULL),
58 pointAttachedToMouse(-1),
59 highlightColor(255, 0, 0),
60 normalColor(Qt::black),
61 animation(this,
"color")
63 setCacheMode(DeviceCoordinateCache);
65 setPen(QPen(
color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
66 setBrush(Qt::Dense3Pattern);
67 setFlag(QGraphicsItem::ItemIsSelectable,
true);
68 setAcceptHoverEvents(
true);
73 transitionLabel->setZValue(100000);
81 return highlightAnimationStartTime;
91 path.addEllipse(initialStateCircle);
95 path.setFillRule(Qt::WindingFill);
110 return QGraphicsPathItem::boundingRect().adjusted(-2, -2, 2, 2) | arrowHead.boundingRect();
116 if (transition && !transition->sourceState)
133 pointAttachedToMouse = -1;
137 if (transition->sourceState)
141 points.
startPoint = std::make_shared<QPointF>(calcIntersection(transition->sourceState, *(points.
startPoint)));
151 points.
startPoint = std::make_shared<QPointF>(calcIntersection(transition->sourceState, points.
controlPoints.first()));
154 else if (transition->destinationState)
157 points.
startPoint = std::make_shared<QPointF>(calcIntersection(transition->sourceState, transition->destinationState->getBounds().center()));
162 points.
startPoint = std::make_shared<QPointF>(calcIntersection(transition->sourceState, *points.
endPoint));
180 if (transition->destinationState)
185 points.
endPoint = std::make_shared<QPointF>(calcIntersection(transition->destinationState, *(points.
endPoint)));
192 calcIntersection(transition->destinationState, points.
controlPoints.last()));
196 ARMARX_DEBUG <<
"inserting control point between last controlpoint and destination state";
201 ARMARX_DEBUG <<
"inserting control point between startPoint and destination state";
219 if (pointList.size() <= 1)
222 pointList.push_front(QPointF(100, 400));
235 initialStateCircle.setTopLeft(QPointF(path.elementAt(0).x - r, path.elementAt(0).y - r));
236 initialStateCircle.setWidth(r * 2);
237 initialStateCircle.setHeight(r * 2);
254 QGraphicsItem::setSelected(selected);
259 pointAttachedToMouse = supportPointIndex;
265 highlightAnimationStartTime = IceUtil::Time::now();
266 animation.setDuration(duration);
270 animation.setKeyValueAt(1, normalColor);
272 animation.setEasingCurve(QEasingCurve::InOutQuad);
280 auto labelText = transition->eventName + (transition->transitionUserCode ?
" (C)" :
"");
281 transitionLabel->setText(labelText);
282 transitionLabel->update();
283 QString eventName = transition->eventName;
284 if (!transition->sourceState)
286 eventName =
"Initial Transition";
288 setToolTip(
"Event: " + eventName +
" - " + (transition->transitionUserCode ?
"Transition with user code" :
"Transition without user code"));
295 auto path = this->path();
298 auto addHull = [&](
bool forward,
float width)
300 float step = 0.0499999;
303 float offsetAngle =
M_PI / 2;
307 offsetAngle = -
M_PI / 2;
312 if (path.length() == 0)
317 for (
float percent = start; percent <= end; percent += step)
319 p2 = path.pointAtPercent(forward ? percent : 1 - percent);
320 float angle = path.angleAtPercent(forward ? percent : 1 - percent) / 180 *
M_PI;
325 float newAngle =
angle + offsetAngle;
327 float x2 = cos(newAngle) * x - sin(newAngle) * y;
328 float y2 = sin(newAngle) * x + cos(newAngle) * y;
329 hull.append(QPointF(x2, y2) + p2);
333 painter->drawEllipse(hull.last(), 2, 2);
341 painter->setPen(QPen(
Qt::red, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
342 painter->setBrush(QBrush(
Qt::red, Qt::NoBrush));
349 painter->setPen(QPen(
Qt::green, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
350 painter->setBrush(QBrush(
Qt::red, Qt::NoBrush));
383 QLineF centerLine(state->getBounds().center(), supportPoint);
384 return calcIntersection(state, centerLine, supportPoint);
390 QPolygonF statePolygon(state->getBounds());
391 QPointF p1 = statePolygon.first();
393 QPointF intersectPoint;
397 for (
int i = 1; i < statePolygon.count(); ++i)
399 p2 = statePolygon.at(i);
400 polyLine = QLineF(p1, p2);
401 QLineF::IntersectType intersectType =
402 polyLine.intersect(centerLine, &intersectPoint);
404 if (intersectType == QLineF::BoundedIntersection)
419 for (
int i = 1; i < statePolygon.count(); ++i)
421 QPointF curIntersectPoint;
422 p2 = statePolygon.at(i);
423 polyLine = QLineF(p1, p2);
424 QLineF::IntersectType intersectType =
425 polyLine.intersect(centerLine, &curIntersectPoint);
427 if (intersectType == QLineF::UnboundedIntersection)
429 float length = QLineF(supportPoint, curIntersectPoint).length();
431 if (length < minLength)
433 intersectPoint = curIntersectPoint;
442 return intersectPoint;
445 QPolygonF TransitionItem::calcArrowHead(
const QLineF& line)
447 qreal arrowSize = 15;
449 double angle = ::acos(line.dx() / line.length());
456 QPointF arrowP1 = line.p1() + QPointF(sin(
angle +
M_PI / 3) * arrowSize,
458 QPointF arrowP2 = line.p1() + QPointF(sin(
angle +
M_PI -
M_PI / 3) * arrowSize,
462 arrowHead << line.p1() << arrowP1 << arrowP2 ;
467 void TransitionItem::recalcLabelPose()
469 if (!transitionLabel)
474 QPainterPath curPath = path();
476 if (curPath.elementCount() < 2)
481 QPointF startPoint = curPath.elementAt(0);
482 QPointF endPoint = curPath.elementAt(curPath.elementCount() - 1);
483 QRectF transitionLabelRect = transitionLabel->boundingRect();
485 float transitionLength = calcLengthOfLine(startPoint, endPoint);
488 if (transitionLength == 0)
490 transitionLength = 1;
492 float labelAngle = 0;
495 if (transition->labelCenterPosition)
498 if (transition->labelFontPointSize)
504 if (transitionLabel->rotation() != 0)
506 transitionLabel->setRotation(0);
508 newPos = *transition->labelCenterPosition - QPointF(transitionLabel->boundingRect().width() * 0.5, transitionLabel->boundingRect().height() * 0.5);
513 float diff = (endPoint.x() - startPoint.x()) / transitionLength;
516 labelAngle = acos(diff);
518 if (labelAngle != labelAngle)
525 if (endPoint.y() - startPoint.y() < 0)
527 labelAngle = -labelAngle;
531 if (endPoint.x() - startPoint.x() < 0)
533 labelAngle =
M_PI + labelAngle;
536 newPos.setX(path().pointAtPercent(0.5).x() - arg * transitionLabelRect.width() * 0.5 * (endPoint.x() - startPoint.x()) / transitionLength);
537 newPos.setY(path().pointAtPercent(0.5).y() - arg * transitionLabelRect.width() * 0.5 * (endPoint.y() - startPoint.y()) / transitionLength);
538 double newAngle = labelAngle * 180.0 /
M_PI;
539 if (newAngle == newAngle)
541 if (transitionLabel->rotation() != newAngle)
543 transitionLabel->setRotation(newAngle);
552 transitionLabel->setPos(newPos);
558 float TransitionItem::calcLengthOfLine(QPointF start, QPointF end)
560 float xDiff = end.x() - start.x();
561 float yDiff = end.y() - start.y();
562 return sqrt(pow(xDiff, 2) + pow(yDiff, 2));
576 QGraphicsView* view =
getView(event);
579 QRectF sceneScale = mapToScene(0.0f, 0.0f, 100.0f, 100.0f).boundingRect();
580 QRectF globalScale = view->mapFromScene(sceneScale).boundingRect();
581 float scale = globalScale.width();
588 transitionLabel->update();
589 if (view && arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
591 view->viewport()->setCursor(Qt::OpenHandCursor);
597 QGraphicsView* view =
getView(event);
600 if (arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
602 view->viewport()->setCursor(Qt::OpenHandCursor);
606 view->viewport()->setCursor(Qt::ArrowCursor);
621 transitionLabel->update();
622 QGraphicsView* view =
getView(event);
625 view->viewport()->setCursor(Qt::ArrowCursor);
632 if (!transition || (!transition->sourceState && !transition->destinationState))
637 StateItem* parentState = qgraphicsitem_cast<StateItem*>(parentItem());
646 painter->setBrush(QBrush(Qt::black, Qt::NoBrush));
647 painter->setPen(QPen(Qt::black, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
649 QColor tempColor =
color;
653 tempColor = Qt::darkGray;
666 else if (transition && !transition->destinationState)
668 tempColor = Qt::darkRed;
673 myPen.setColor(tempColor);
675 painter->setPen(myPen);
676 painter->setBrush(QBrush(tempColor, Qt::NoBrush));
677 setBrush(QBrush(tempColor, Qt::NoBrush));
678 setPen(QPen(tempColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
679 painter->drawPath(path());
683 myPen.setColor(tempColor);
685 painter->setPen(myPen);
686 painter->setBrush(tempColor);
687 QPainterPath p = path();
689 if (p.elementCount() < 2)
694 QPointF p1 = p.elementAt(p.elementCount() - 1);
695 QPointF p2 = p.elementAt(p.elementCount() - 2);
698 arrowHead = calcArrowHead(line);
699 painter->drawPolygon(arrowHead);
702 if (!transition->sourceState)
704 painter->drawEllipse(initialStateCircle);
710 painter->setPen(QPen(Qt::gray, 1, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin));
711 painter->setBrush(QBrush(
Qt::red, Qt::NoBrush));
722 if (event->button() == Qt::LeftButton)
724 if (arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
726 pointAttachedToMouse = path().elementCount() - 1;
730 QPainterPath curPath = path();
732 for (
int i = 1; i < curPath.elementCount() - 1; i++)
734 QPointF point = curPath.elementAt(i);
735 QRectF rect(point.x() - 5, point.y() - 5, 10, 10);
737 if (rect.contains(event->pos()))
739 pointAttachedToMouse = i;
740 ARMARX_INFO_S <<
VAROUT(rect) <<
" attached to point " << pointAttachedToMouse <<
"/" << curPath.elementCount();
746 else if (event->button() == Qt::RightButton)
749 scene()->clearSelection();
750 QMetaObject::invokeMethod(
this,
"setSelected", Qt::QueuedConnection, Q_ARG(
bool,
true));
754 QGraphicsPathItem::mousePressEvent(event);
763 if ((event->buttonDownPos(Qt::LeftButton) - event->pos()).manhattanLength() > 10)
767 if (pointAttachedToMouse >= 0 && pointAttachedToMouse < path().elementCount())
770 QPainterPath p = path();
771 p.setElementPositionAt(pointAttachedToMouse, event->pos().x(), event->pos().y());
778 QGraphicsPathItem::mouseMoveEvent(event);
784 if ((event->buttonDownPos(Qt::LeftButton) - event->pos()).manhattanLength() > 10 &&
785 pointAttachedToMouse == path().elementCount() - 1)
791 pointAttachedToMouse = -1;
792 QGraphicsPathItem::mouseReleaseEvent(event);
804 QGraphicsSimpleTextItem(labelText, parent),
806 animation(this,
"fontPointSize")
819 StateItem* parentState = qgraphicsitem_cast<StateItem*>(parentItem()->parentItem());
845 QGraphicsSimpleTextItem::paint(painter,
option, widget);
850 return font().pointSizeF();
866 QFont currentFont = font();
867 currentFont.setPointSizeF(newFontSize);
868 setFont(currentFont);
891 animation.setEasingCurve(QEasingCurve::InOutQuad);