29 #include <QGraphicsScene>
30 #include <QGraphicsSceneMouseEvent>
31 #include <QGraphicsView>
35 #include <QPropertyAnimation>
36 #include <QStyleOptionGraphicsItem>
42 #include "../model/Transition.h"
43 #include "../model/stateinstance/StateInstance.h"
48 #define INITIAL_CIRCLE_RADIUS 10
54 QGraphicsItem* parent) :
55 QGraphicsPathItem(parent),
57 transition(transition),
58 transitionLabel(NULL),
59 pointAttachedToMouse(-1),
60 highlightColor(255, 0, 0),
61 normalColor(Qt::black),
62 animation(this,
"color")
64 setCacheMode(DeviceCoordinateCache);
66 setPen(QPen(
color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
67 setBrush(Qt::Dense3Pattern);
68 setFlag(QGraphicsItem::ItemIsSelectable,
true);
69 setAcceptHoverEvents(
true);
74 transitionLabel->setZValue(100000);
83 return highlightAnimationStartTime;
94 path.addEllipse(initialStateCircle);
98 path.setFillRule(Qt::WindingFill);
109 return QGraphicsPathItem::boundingRect().adjusted(
111 arrowHead.boundingRect();
115 return QGraphicsPathItem::boundingRect().adjusted(-2, -2, 2, 2) |
116 arrowHead.boundingRect();
123 if (transition && !transition->sourceState)
142 pointAttachedToMouse = -1;
146 if (transition->sourceState)
150 points.
startPoint = std::make_shared<QPointF>(
151 calcIntersection(transition->sourceState, *(points.
startPoint)));
157 0, calcIntersection(transition->sourceState, points.
controlPoints.first()));
162 points.
startPoint = std::make_shared<QPointF>(
163 calcIntersection(transition->sourceState, points.
controlPoints.first()));
171 points.
startPoint = std::make_shared<QPointF>(calcIntersection(
172 transition->sourceState, transition->destinationState->getBounds().center()));
177 points.
startPoint = std::make_shared<QPointF>(
178 calcIntersection(transition->sourceState, *points.
endPoint));
195 if (transition->destinationState)
200 points.
endPoint = std::make_shared<QPointF>(
201 calcIntersection(transition->destinationState, *(points.
endPoint)));
209 calcIntersection(transition->destinationState, points.
controlPoints.last()));
214 <<
"inserting control point between last controlpoint and destination state";
216 calcIntersection(transition->destinationState, points.
controlPoints.last()));
220 ARMARX_DEBUG <<
"inserting control point between startPoint and destination state";
222 calcIntersection(transition->destinationState, *points.
startPoint));
239 if (pointList.size() <= 1)
242 pointList.push_front(QPointF(100, 400));
253 initialStateCircle.setTopLeft(
254 QPointF(path.elementAt(0).x - r, path.elementAt(0).y - r));
255 initialStateCircle.setWidth(r * 2);
256 initialStateCircle.setHeight(r * 2);
274 QGraphicsItem::setSelected(selected);
280 pointAttachedToMouse = supportPointIndex;
287 highlightAnimationStartTime = IceUtil::Time::now();
288 animation.setDuration(duration);
292 animation.setKeyValueAt(1, normalColor);
294 animation.setEasingCurve(QEasingCurve::InOutQuad);
303 auto labelText = transition->eventName + (transition->transitionUserCode ?
" (C)" :
"");
304 transitionLabel->setText(labelText);
305 transitionLabel->update();
306 QString eventName = transition->eventName;
307 if (!transition->sourceState)
309 eventName =
"Initial Transition";
311 setToolTip(
"Event: " + eventName +
" - " +
312 (transition->transitionUserCode ?
"Transition with user code"
313 :
"Transition without user code"));
320 auto path = this->path();
323 auto addHull = [&](
bool forward,
float width)
325 float step = 0.0499999;
328 float offsetAngle =
M_PI / 2;
332 offsetAngle = -
M_PI / 2;
337 if (path.length() == 0)
342 for (
float percent = start; percent <= end; percent += step)
344 p2 = path.pointAtPercent(forward ? percent : 1 - percent);
345 float angle = path.angleAtPercent(forward ? percent : 1 - percent) / 180 *
M_PI;
350 float newAngle =
angle + offsetAngle;
352 float x2 = cos(newAngle) * x - sin(newAngle) * y;
353 float y2 = sin(newAngle) * x + cos(newAngle) * y;
354 hull.append(QPointF(x2, y2) + p2);
358 painter->drawEllipse(hull.last(), 2, 2);
365 painter->setPen(QPen(
Qt::red, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
366 painter->setBrush(QBrush(
Qt::red, Qt::NoBrush));
373 painter->setPen(QPen(
Qt::green, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
374 painter->setBrush(QBrush(
Qt::red, Qt::NoBrush));
410 const QPointF& supportPoint)
const
412 QLineF centerLine(state->getBounds().center(), supportPoint);
413 return calcIntersection(state, centerLine, supportPoint);
419 const QPointF& supportPoint)
const
422 QPolygonF statePolygon(state->getBounds());
423 QPointF p1 = statePolygon.first();
425 QPointF intersectPoint;
429 for (
int i = 1; i < statePolygon.count(); ++i)
431 p2 = statePolygon.at(i);
432 polyLine = QLineF(p1, p2);
433 QLineF::IntersectType intersectType = polyLine.intersect(centerLine, &intersectPoint);
435 if (intersectType == QLineF::BoundedIntersection)
450 for (
int i = 1; i < statePolygon.count(); ++i)
452 QPointF curIntersectPoint;
453 p2 = statePolygon.at(i);
454 polyLine = QLineF(p1, p2);
455 QLineF::IntersectType intersectType =
456 polyLine.intersect(centerLine, &curIntersectPoint);
458 if (intersectType == QLineF::UnboundedIntersection)
460 float length = QLineF(supportPoint, curIntersectPoint).length();
462 if (length < minLength)
464 intersectPoint = curIntersectPoint;
473 return intersectPoint;
477 TransitionItem::calcArrowHead(
const QLineF& line)
479 qreal arrowSize = 15;
481 double angle = ::acos(line.dx() / line.length());
488 QPointF arrowP1 = line.p1() + QPointF(sin(
angle +
M_PI / 3) * arrowSize,
490 QPointF arrowP2 = line.p1() + QPointF(sin(
angle +
M_PI -
M_PI / 3) * arrowSize,
494 arrowHead << line.p1() << arrowP1 << arrowP2;
500 TransitionItem::recalcLabelPose()
502 if (!transitionLabel)
507 QPainterPath curPath = path();
509 if (curPath.elementCount() < 2)
514 QPointF startPoint = curPath.elementAt(0);
515 QPointF endPoint = curPath.elementAt(curPath.elementCount() - 1);
516 QRectF transitionLabelRect = transitionLabel->boundingRect();
518 float transitionLength = calcLengthOfLine(startPoint, endPoint);
521 if (transitionLength == 0)
523 transitionLength = 1;
525 float labelAngle = 0;
528 if (transition->labelCenterPosition)
531 if (transition->labelFontPointSize)
537 if (transitionLabel->rotation() != 0)
539 transitionLabel->setRotation(0);
541 newPos = *transition->labelCenterPosition -
542 QPointF(transitionLabel->boundingRect().width() * 0.5,
543 transitionLabel->boundingRect().height() * 0.5);
548 float diff = (endPoint.x() - startPoint.x()) / transitionLength;
551 labelAngle = acos(diff);
553 if (labelAngle != labelAngle)
560 if (endPoint.y() - startPoint.y() < 0)
562 labelAngle = -labelAngle;
566 if (endPoint.x() - startPoint.x() < 0)
568 labelAngle =
M_PI + labelAngle;
571 newPos.setX(path().pointAtPercent(0.5).x() - arg * transitionLabelRect.width() * 0.5 *
572 (endPoint.x() - startPoint.x()) /
574 newPos.setY(path().pointAtPercent(0.5).y() - arg * transitionLabelRect.width() * 0.5 *
575 (endPoint.y() - startPoint.y()) /
577 double newAngle = labelAngle * 180.0 /
M_PI;
578 if (newAngle == newAngle)
580 if (transitionLabel->rotation() != newAngle)
582 transitionLabel->setRotation(newAngle);
591 transitionLabel->setPos(newPos);
595 TransitionItem::calcLengthOfLine(QPointF start, QPointF end)
597 float xDiff = end.x() - start.x();
598 float yDiff = end.y() - start.y();
599 return sqrt(pow(xDiff, 2) + pow(yDiff, 2));
615 QGraphicsView* view =
getView(event);
618 QRectF sceneScale = mapToScene(0.0f, 0.0f, 100.0f, 100.0f).boundingRect();
619 QRectF globalScale = view->mapFromScene(sceneScale).boundingRect();
620 float scale = globalScale.width();
627 transitionLabel->update();
628 if (view && arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
630 view->viewport()->setCursor(Qt::OpenHandCursor);
637 QGraphicsView* view =
getView(event);
640 if (arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
642 view->viewport()->setCursor(Qt::OpenHandCursor);
646 view->viewport()->setCursor(Qt::ArrowCursor);
662 transitionLabel->update();
663 QGraphicsView* view =
getView(event);
666 view->viewport()->setCursor(Qt::ArrowCursor);
672 const QStyleOptionGraphicsItem*
option,
676 if (!transition || (!transition->sourceState && !transition->destinationState))
681 StateItem* parentState = qgraphicsitem_cast<StateItem*>(parentItem());
690 painter->setBrush(QBrush(Qt::black, Qt::NoBrush));
691 painter->setPen(QPen(Qt::black, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
693 QColor tempColor =
color;
697 tempColor = Qt::darkGray;
709 else if (transition && !transition->destinationState)
711 tempColor = Qt::darkRed;
716 myPen.setColor(tempColor);
718 painter->setPen(myPen);
719 painter->setBrush(QBrush(tempColor, Qt::NoBrush));
720 setBrush(QBrush(tempColor, Qt::NoBrush));
721 setPen(QPen(tempColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
722 painter->drawPath(path());
726 myPen.setColor(tempColor);
728 painter->setPen(myPen);
729 painter->setBrush(tempColor);
730 QPainterPath p = path();
732 if (p.elementCount() < 2)
737 QPointF p1 = p.elementAt(p.elementCount() - 1);
738 QPointF p2 = p.elementAt(p.elementCount() - 2);
741 arrowHead = calcArrowHead(line);
742 painter->drawPolygon(arrowHead);
745 if (!transition->sourceState)
747 painter->drawEllipse(initialStateCircle);
753 painter->setPen(QPen(Qt::gray, 1, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin));
754 painter->setBrush(QBrush(
Qt::red, Qt::NoBrush));
763 if (event->button() == Qt::LeftButton)
765 if (arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
767 pointAttachedToMouse = path().elementCount() - 1;
771 QPainterPath curPath = path();
773 for (
int i = 1; i < curPath.elementCount() - 1; i++)
775 QPointF point = curPath.elementAt(i);
776 QRectF rect(point.x() - 5, point.y() - 5, 10, 10);
778 if (rect.contains(event->pos()))
780 pointAttachedToMouse = i;
782 << pointAttachedToMouse <<
"/" << curPath.elementCount();
788 else if (event->button() == Qt::RightButton)
791 scene()->clearSelection();
792 QMetaObject::invokeMethod(
this,
"setSelected", Qt::QueuedConnection, Q_ARG(
bool,
true));
796 QGraphicsPathItem::mousePressEvent(event);
806 if ((event->buttonDownPos(Qt::LeftButton) - event->pos()).manhattanLength() > 10)
810 if (pointAttachedToMouse >= 0 && pointAttachedToMouse < path().elementCount())
813 QPainterPath p = path();
814 p.setElementPositionAt(pointAttachedToMouse, event->pos().x(), event->pos().y());
821 QGraphicsPathItem::mouseMoveEvent(event);
828 if ((event->buttonDownPos(Qt::LeftButton) - event->pos()).manhattanLength() > 10 &&
829 pointAttachedToMouse == path().elementCount() - 1)
835 pointAttachedToMouse = -1;
836 QGraphicsPathItem::mouseReleaseEvent(event);
846 QGraphicsSimpleTextItem(labelText, parent),
848 animation(this,
"fontPointSize")
859 const QStyleOptionGraphicsItem*
option,
864 StateItem* parentState = qgraphicsitem_cast<StateItem*>(parentItem()->parentItem());
890 QGraphicsSimpleTextItem::paint(painter,
option, widget);
896 return font().pointSizeF();
915 QFont currentFont = font();
916 currentFont.setPointSizeF(newFontSize);
917 setFont(currentFont);
942 animation.setEasingCurve(QEasingCurve::InOutQuad);