TransitionItem.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24#include "TransitionItem.h"
25
26#include <cmath>
27#include <memory>
28
29#include <QGraphicsScene>
30#include <QGraphicsSceneMouseEvent>
31#include <QGraphicsView>
32#include <QMenu>
33#include <QPainter>
34#include <QPen>
35#include <QPropertyAnimation>
36#include <QStyleOptionGraphicsItem>
37
39
41
42#include "../model/Transition.h"
44#include "SplinePath.h"
45#include "StateItem.h"
46
47
48#define INITIAL_CIRCLE_RADIUS 10
49
50namespace armarx
51{
52
54 QGraphicsItem* parent) :
55 QGraphicsPathItem(parent),
56 MorphingItem(this),
57 transition(transition),
58 transitionLabel(NULL),
59 pointAttachedToMouse(-1),
60 highlightColor(255, 0, 0),
61 normalColor(Qt::black),
62 animation(this, "color")
63 {
64 setCacheMode(DeviceCoordinateCache);
65 color = Qt::black;
66 setPen(QPen(color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
67 setBrush(Qt::Dense3Pattern);
68 setFlag(QGraphicsItem::ItemIsSelectable, true);
69 setAcceptHoverEvents(true);
70 if (transition)
71 {
72 transitionLabel = new TransitionLabel("", this);
73
74 transitionLabel->setZValue(100000);
76 }
77 setZValue(100000);
78 }
79
80 IceUtil::Time
82 {
83 return highlightAnimationStartTime;
84 }
85
86 QPainterPath
88 {
89
90 QPainterPath path;
91
93 {
94 path.addEllipse(initialStateCircle);
95 }
96
97 path.addPolygon(shapePolygon);
98 path.setFillRule(Qt::WindingFill);
99 return path;
100 }
101
102 QRectF
104 {
105
106
108 {
109 return QGraphicsPathItem::boundingRect().adjusted(
111 arrowHead.boundingRect();
112 }
113 else
114 {
115 return QGraphicsPathItem::boundingRect().adjusted(-2, -2, 2, 2) |
116 arrowHead.boundingRect();
117 }
118 }
119
120 bool
122 {
123 if (transition && !transition->sourceState)
124 {
125 return true;
126 }
127
128 return false;
129 }
130
131 void
133 {
134 // TODO
135 }
136
137 void
139 {
140 ARMARX_CHECK_EXPRESSION(transition != NULL);
141
142 pointAttachedToMouse = -1;
143 SupportPoints points = transition->supportPoints;
144 // ARMARX_DEBUG << points.controlPoints.size() << " points at start";
145 //adapt the first point
146 if (transition->sourceState)
147 {
148 if (points.startPoint)
149 {
150 points.startPoint = std::make_shared<QPointF>(
151 calcIntersection(transition->sourceState, *(points.startPoint)));
152 }
153 else if (points.controlPoints.size() > 1)
154 {
155 //points.startPoint = std::make_shared<QPointF>(calcIntersection(transition->sourceState, points.controlPoints.first()));
156 points.controlPoints.replace(
157 0, calcIntersection(transition->sourceState, points.controlPoints.first()));
158 }
159 else if (points.controlPoints.size() == 1)
160 {
161 // ARMARX_DEBUG << "setting startpoint straight between sourceState and sfirst controlpoint";
162 points.startPoint = std::make_shared<QPointF>(
163 calcIntersection(transition->sourceState, points.controlPoints.first()));
164 //points.controlPoints.replace(0, calcIntersection(transition->sourceState, points.controlPoints.first()));
165 }
166 else if (
167 transition
168 ->destinationState) // calc start point so that it lies on the edge of the source state
169 {
170 // ARMARX_DEBUG << "adding controlpoint straight between dest state and source on edge of source";
171 points.startPoint = std::make_shared<QPointF>(calcIntersection(
172 transition->sourceState, transition->destinationState->getBounds().center()));
173 //points.controlPoints.push_front(calcIntersection(transition->sourceState, transition->destinationState->getBounds().center()));
174 }
175 else if (points.endPoint)
176 {
177 points.startPoint = std::make_shared<QPointF>(
178 calcIntersection(transition->sourceState, *points.endPoint));
179 // points.controlPoints.push_front(calcIntersection(transition->sourceState, *points.endPoint));
180 }
181 }
182 else
183 {
184 // this is the start transition
185 // if(transition->destinationState)
186 // points.controlPoints.push_back(transition->destinationState->getBounds().center());
187 if (points.controlPoints.size() == 0)
188 {
189 points.controlPoints.push_back(QPointF(50, 80));
190 }
191 assert(!(points.startPoint));
192 }
193
194 //adapt the last point
195 if (transition->destinationState)
196 {
197 if (points.endPoint)
198 {
199 ARMARX_DEBUG << "Adjusting start point";
200 points.endPoint = std::make_shared<QPointF>(
201 calcIntersection(transition->destinationState, *(points.endPoint)));
202 }
203 else if (points.controlPoints.size() > 2)
204 {
205 ARMARX_DEBUG << "Adjusting first control point";
206 //replace the last control point
207 points.controlPoints.replace(
208 points.controlPoints.size() - 1,
209 calcIntersection(transition->destinationState, points.controlPoints.last()));
210 }
211 else if (points.controlPoints.size() >= 1)
212 {
214 << "inserting control point between last controlpoint and destination state";
215 points.controlPoints.push_back(
216 calcIntersection(transition->destinationState, points.controlPoints.last()));
217 }
218 else if (points.startPoint)
219 {
220 ARMARX_DEBUG << "inserting control point between startPoint and destination state";
221 points.controlPoints.push_back(
222 calcIntersection(transition->destinationState, *points.startPoint));
223 }
224 }
225 else
226 {
227 //this is a detached transition
228 //just keep control points as they were calculated otherwhere
229 }
230
231 // Not working - why?
232 // if(transition->sourceState && points.controlPoints.size() > 1)
233 // {
234 // points.controlPoints.replace(0, calcIntersection(transition->sourceState, points.controlPoints.first()));
235 // }
236
237 //calculate path using the spline class
238 auto pointList = points.toPointList();
239 if (pointList.size() <= 1)
240 {
241 //this is the start transition in the initial creation
242 pointList.push_front(QPointF(100, 400));
243 }
244 QPainterPath path = SplinePath::simplePath(pointList);
245 //TODO: simplePath geht davon aus, dass immer 2 oder n = 1 mod 3 Kontrollpunkte gegeben sind.
246 // Ist das immer gegeben?
247
248
249 if (isInitialTransition() && path.elementCount() > 0)
250 {
251 // draw initial start circle
252 int r = 10;
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);
257 }
258
259 setPath(path);
260 recalcLabelPose();
261
262 recalcShape();
263 }
264
265 void
270
271 void
273 {
274 QGraphicsItem::setSelected(selected);
275 }
276
277 void
278 TransitionItem::attachToMouse(int supportPointIndex)
279 {
280 pointAttachedToMouse = supportPointIndex;
281 }
282
283 void
285 {
286 animation.stop();
287 highlightAnimationStartTime = IceUtil::Time::now();
288 animation.setDuration(duration);
289 animation.setKeyValueAt(0, highlightColor);
290 // animation.setKeyValueAt(0.3, normalColor);
291 // animation.setKeyValueAt(0.99, QColor(255, 0, 0));
292 animation.setKeyValueAt(1, normalColor);
293
294 animation.setEasingCurve(QEasingCurve::InOutQuad);
295 animation.start();
296 }
297
298 void
300 {
301 if (transitionLabel)
302 {
303 auto labelText = transition->eventName + (transition->transitionUserCode ? " (C)" : "");
304 transitionLabel->setText(labelText);
305 transitionLabel->update();
306 QString eventName = transition->eventName;
307 if (!transition->sourceState)
308 {
309 eventName = "Initial Transition";
310 }
311 setToolTip("Event: " + eventName + " - " +
312 (transition->transitionUserCode ? "Transition with user code"
313 : "Transition without user code"));
314 }
315 }
316
317 QPolygonF
318 TransitionItem::calcHull(QPainter* painter) const
319 {
320 auto path = this->path();
321 QPolygonF hull;
322
323 auto addHull = [&](bool forward, float width)
324 {
325 float step = 0.0499999;
326 float start = 0;
327 float end = 1;
328 float offsetAngle = M_PI / 2;
329
330 if (!forward)
331 {
332 offsetAngle = -M_PI / 2;
333 }
334
335 QPointF p2;
336
337 if (path.length() == 0)
338 {
339 return;
340 }
341
342 for (float percent = start; percent <= end; percent += step)
343 {
344 p2 = path.pointAtPercent(forward ? percent : 1 - percent);
345 float angle = path.angleAtPercent(forward ? percent : 1 - percent) / 180 * M_PI;
346 // float angle = getAngle(p,p2);
347
348 float x = width;
349 float y = 0;
350 float newAngle = angle + offsetAngle;
351 newAngle *= -1;
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);
355
356 if (painter)
357 {
358 painter->drawEllipse(hull.last(), 2, 2);
359 }
360 }
361 };
362
363 if (painter)
364 {
365 painter->setPen(QPen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
366 painter->setBrush(QBrush(Qt::red, Qt::NoBrush));
367 }
368
369 addHull(true, 5);
370
371 if (painter)
372 {
373 painter->setPen(QPen(Qt::green, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
374 painter->setBrush(QBrush(Qt::red, Qt::NoBrush));
375 }
376
377 addHull(false, 5);
378
379
380 return hull;
381 }
382
383 QColor
385 {
386 return this->color;
387 }
388
389 void
391 {
392 this->color = color;
393 update();
394 }
395
396 QColor
398 {
399 return highlightColor;
400 }
401
402 void
404 {
405 this->highlightColor = color;
406 }
407
408 QPointF
409 TransitionItem::calcIntersection(statechartmodel::StateInstancePtr state,
410 const QPointF& supportPoint) const
411 {
412 QLineF centerLine(state->getBounds().center(), supportPoint);
413 return calcIntersection(state, centerLine, supportPoint);
414 }
415
416 QPointF
417 TransitionItem::calcIntersection(statechartmodel::StateInstancePtr state,
418 QLineF centerLine,
419 const QPointF& supportPoint) const
420 {
421
422 QPolygonF statePolygon(state->getBounds());
423 QPointF p1 = statePolygon.first();
424 QPointF p2;
425 QPointF intersectPoint;
426 QLineF polyLine;
427 bool found = false;
428
429 for (int i = 1; i < statePolygon.count(); ++i)
430 {
431 p2 = statePolygon.at(i);
432 polyLine = QLineF(p1, p2);
433 QLineF::IntersectType intersectType = polyLine.intersect(centerLine, &intersectPoint);
434
435 if (intersectType == QLineF::BoundedIntersection)
436 {
437 found = true;
438 break;
439 }
440
441 p1 = p2;
442 }
443
444
445 if (!found)
446 {
447 // support point is IN the state
448 float minLength = std::numeric_limits<float>::max();
449
450 for (int i = 1; i < statePolygon.count(); ++i)
451 {
452 QPointF curIntersectPoint;
453 p2 = statePolygon.at(i);
454 polyLine = QLineF(p1, p2);
455 QLineF::IntersectType intersectType =
456 polyLine.intersect(centerLine, &curIntersectPoint);
457
458 if (intersectType == QLineF::UnboundedIntersection)
459 {
460 float length = QLineF(supportPoint, curIntersectPoint).length();
461
462 if (length < minLength)
463 {
464 intersectPoint = curIntersectPoint;
465 minLength = length;
466 }
467 }
468
469 p1 = p2;
470 }
471 }
472
473 return intersectPoint;
474 }
475
476 QPolygonF
477 TransitionItem::calcArrowHead(const QLineF& line)
478 {
479 qreal arrowSize = 15;
480
481 double angle = ::acos(line.dx() / line.length());
482
483 if (line.dy() >= 0)
484 {
485 angle = (M_PI * 2) - angle;
486 }
487
488 QPointF arrowP1 = line.p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,
489 cos(angle + M_PI / 3) * arrowSize);
490 QPointF arrowP2 = line.p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
491 cos(angle + M_PI - M_PI / 3) * arrowSize);
492
493 QPolygonF arrowHead;
494 arrowHead << line.p1() << arrowP1 << arrowP2;
495
496 return arrowHead;
497 }
498
499 void
500 TransitionItem::recalcLabelPose()
501 {
502 if (!transitionLabel)
503 {
504 return;
505 }
506
507 QPainterPath curPath = path();
508
509 if (curPath.elementCount() < 2)
510 {
511 return;
512 }
513
514 QPointF startPoint = curPath.elementAt(0);
515 QPointF endPoint = curPath.elementAt(curPath.elementCount() - 1);
516 QRectF transitionLabelRect = transitionLabel->boundingRect();
517
518 float transitionLength = calcLengthOfLine(startPoint, endPoint);
519 ARMARX_CHECK_EXPRESSION(transitionLength == transitionLength);
520
521 if (transitionLength == 0)
522 {
523 transitionLength = 1;
524 }
525 float labelAngle = 0;
526 //set position and angle of the transition label
527 QPointF newPos;
528 if (transition->labelCenterPosition)
529 {
530 // ARMARX_INFO_S << "Using precalculated label position";
531 if (transition->labelFontPointSize)
532 {
533 // ARMARX_INFO_S << "Using precalculated label font size";
534 transitionLabel->setBaseFontPointSize(*transition->labelFontPointSize);
535 transitionLabel->setFontPointSize(*transition->labelFontPointSize);
536 }
537 if (transitionLabel->rotation() != 0)
538 {
539 transitionLabel->setRotation(0);
540 }
541 newPos = *transition->labelCenterPosition -
542 QPointF(transitionLabel->boundingRect().width() * 0.5,
543 transitionLabel->boundingRect().height() * 0.5);
544 }
545 else
546 {
547 //angle between x direction and edge
548 float diff = (endPoint.x() - startPoint.x()) / transitionLength;
549 diff = std::max(-1.0f, diff);
550 diff = std::min(1.0f, diff);
551 labelAngle = acos(diff);
552
553 if (labelAngle != labelAngle)
554 {
555 ARMARX_WARNING << VAROUT(diff) << "; " << VAROUT(acos(diff));
556 }
557
558 int arg = 1;
559
560 if (endPoint.y() - startPoint.y() < 0)
561 {
562 labelAngle = -labelAngle; //angle is negative if end is lower than start
563 }
564
565 //adjust angle if end is to the left of start
566 if (endPoint.x() - startPoint.x() < 0)
567 {
568 labelAngle = M_PI + labelAngle;
569 arg = -1;
570 }
571 newPos.setX(path().pointAtPercent(0.5).x() - arg * transitionLabelRect.width() * 0.5 *
572 (endPoint.x() - startPoint.x()) /
573 transitionLength);
574 newPos.setY(path().pointAtPercent(0.5).y() - arg * transitionLabelRect.width() * 0.5 *
575 (endPoint.y() - startPoint.y()) /
576 transitionLength);
577 double newAngle = labelAngle * 180.0 / M_PI;
578 if (newAngle == newAngle)
579 {
580 if (transitionLabel->rotation() != newAngle)
581 {
582 transitionLabel->setRotation(newAngle);
583 }
584 }
585 else
586 {
587 ARMARX_WARNING << "Angle is NaN";
588 }
589 }
590
591 transitionLabel->setPos(newPos);
592 }
593
594 float
595 TransitionItem::calcLengthOfLine(QPointF start, QPointF end)
596 {
597 float xDiff = end.x() - start.x();
598 float yDiff = end.y() - start.y();
599 return sqrt(pow(xDiff, 2) + pow(yDiff, 2));
600 }
601
604 {
605 return transition;
606 }
607
608 void
609 TransitionItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
610 {
611 // ARMARX_INFO << "hoverEnterEvent " << transition->eventName;
612 // QFont font = transitionLabel->font();
613 // font.setPointSizeF(font.pointSizeF() * 2);
614 // transitionLabel->setFont(font);
615 QGraphicsView* view = getView(event);
616 if (view)
617 {
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();
621 float newFontSize = transitionLabel->baseFontPointSize() / (scale / 100);
622 transitionLabel->setEnlargedFontPointSize(newFontSize);
623 }
624 setColor(Qt::gray);
625 update();
626 transitionLabel->setHovering(true);
627 transitionLabel->update();
628 if (view && arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
629 {
630 view->viewport()->setCursor(Qt::OpenHandCursor);
631 }
632 }
633
634 void
635 TransitionItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
636 {
637 QGraphicsView* view = getView(event);
638 if (view)
639 {
640 if (arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
641 {
642 view->viewport()->setCursor(Qt::OpenHandCursor);
643 }
644 else
645 {
646 view->viewport()->setCursor(Qt::ArrowCursor);
647 }
648 }
649 }
650
651 void
652 TransitionItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
653 {
654 // ARMARX_INFO << "hoverLeaveEvent " << transition->eventName;
655 // QFont font = transitionLabel->font();
656 // font.setPointSizeF(font.pointSizeF() / 2);
657 // transitionLabel->setFont(font);
658 setColor(normalColor);
659 update();
660 transitionLabel->setHovering(false);
661
662 transitionLabel->update();
663 QGraphicsView* view = getView(event);
664 if (view)
665 {
666 view->viewport()->setCursor(Qt::ArrowCursor);
667 }
668 }
669
670 void
671 TransitionItem::paint(QPainter* painter,
672 const QStyleOptionGraphicsItem* option,
673 QWidget* widget)
674 {
675 // ARMARX_INFO << "painting transition";
676 if (!transition || (!transition->sourceState && !transition->destinationState))
677 {
678 return;
679 }
680
681 StateItem* parentState = qgraphicsitem_cast<StateItem*>(parentItem());
682 LevelOfDetail detail = getLevelOfDetailByParent(painter, parentState);
683
684 if (detail == eHidden)
685 // only use scale of parent so that all sub elements of parent (states, transitions) are hidden at once and not one after the other
686 {
687 return;
688 }
689
690 painter->setBrush(QBrush(Qt::black, Qt::NoBrush));
691 painter->setPen(QPen(Qt::black, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
692 recalcLabelPose();
693 QColor tempColor = color;
694
695 if (isSelected())
696 {
697 tempColor = Qt::darkGray;
698 // tempColor = tempColor.darker();
699 // QPainterPath curPath = path();
700 // for(int i = 1; i < curPath.elementCount()-1; i++)
701 // {
702 // painter->drawEllipse(curPath.elementAt(i), 1,1);
703 // }
704 }
705 // else if (option->state & QStyle::State_MouseOver)
706 // {
707 // ARMARX_INFO << "Mouse over transition";
708 // }
709 else if (transition && !transition->destinationState)
710 {
711 tempColor = Qt::darkRed;
712 }
713
714
715 QPen myPen = pen();
716 myPen.setColor(tempColor);
717
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());
723 // QGraphicsPathItem::paint(painter, option, widget);
724 // QPen myPen = pen();
725
726 myPen.setColor(tempColor);
727
728 painter->setPen(myPen);
729 painter->setBrush(tempColor);
730 QPainterPath p = path();
731
732 if (p.elementCount() < 2)
733 {
734 return;
735 }
736
737 QPointF p1 = p.elementAt(p.elementCount() - 1);
738 QPointF p2 = p.elementAt(p.elementCount() - 2);
739 QLineF line(p1, p2);
740
741 arrowHead = calcArrowHead(line);
742 painter->drawPolygon(arrowHead);
743
744
745 if (!transition->sourceState) // circle for start state transition
746 {
747 painter->drawEllipse(initialStateCircle);
748 }
749
750 // auto hull = calcHull(0);
751 if (isSelected())
752 {
753 painter->setPen(QPen(Qt::gray, 1, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin));
754 painter->setBrush(QBrush(Qt::red, Qt::NoBrush));
755 painter->drawPolygon(shapePolygon);
756 }
757 }
758
759 void
760 TransitionItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
761 {
762 // ARMARX_INFO_S << "mouse pos: " << event->pos();
763 if (event->button() == Qt::LeftButton)
764 {
765 if (arrowHead.containsPoint(event->pos(), Qt::OddEvenFill))
766 {
767 pointAttachedToMouse = path().elementCount() - 1;
768 }
769 else
770 {
771 QPainterPath curPath = path();
772
773 for (int i = 1; i < curPath.elementCount() - 1; i++)
774 {
775 QPointF point = curPath.elementAt(i);
776 QRectF rect(point.x() - 5, point.y() - 5, 10, 10);
777
778 if (rect.contains(event->pos()))
779 {
780 pointAttachedToMouse = i;
781 ARMARX_INFO_S << VAROUT(rect) << " attached to point "
782 << pointAttachedToMouse << "/" << curPath.elementCount();
783 break;
784 }
785 }
786 }
787 }
788 else if (event->button() == Qt::RightButton)
789 {
790 event->accept();
791 scene()->clearSelection();
792 QMetaObject::invokeMethod(this, "setSelected", Qt::QueuedConnection, Q_ARG(bool, true));
793 }
794 else
795 {
796 QGraphicsPathItem::mousePressEvent(event);
797 }
798
799 // ARMARX_INFO_S << VAROUT(pointAttachedToMouse);
800 }
801
802 void
803 TransitionItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
804 {
805 // ARMARX_INFO_S << "buttonDownPos dist: " << (event->buttonDownPos(Qt::LeftButton)-event->pos()).manhattanLength() ;
806 if ((event->buttonDownPos(Qt::LeftButton) - event->pos()).manhattanLength() > 10)
807 {
808
809 // ARMARX_INFO_S << VAROUT(event->pos()) << ": " << VAROUT(pointAttachedToMouse);
810 if (pointAttachedToMouse >= 0 && pointAttachedToMouse < path().elementCount())
811 {
812 // ARMARX_INFO_S << VAROUT(path().elementCount()) << ": " << VAROUT(pointAttachedToMouse);
813 QPainterPath p = path();
814 p.setElementPositionAt(pointAttachedToMouse, event->pos().x(), event->pos().y());
815 setPath(p);
816 recalcLabelPose();
817 recalcShape();
818 }
819 }
820
821 QGraphicsPathItem::mouseMoveEvent(event);
822 }
823
824 void
825 TransitionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
826 {
827
828 if ((event->buttonDownPos(Qt::LeftButton) - event->pos()).manhattanLength() > 10 &&
829 pointAttachedToMouse == path().elementCount() - 1)
830 {
831 emit transitionEndPlaced(transition, event->pos());
832 recalcLabelPose();
833 }
834
835 pointAttachedToMouse = -1;
836 QGraphicsPathItem::mouseReleaseEvent(event);
837 }
838
839 void
840 TransitionItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
841 {
842 emit transitionContextMenuRequested(transition, event->screenPos(), event->pos());
843 }
844
845 TransitionLabel::TransitionLabel(QString labelText, QGraphicsItem* parent) :
846 QGraphicsSimpleTextItem(labelText, parent),
847 MorphingItem(this),
848 animation(this, "fontPointSize")
849 {
851 // QFont currentFont = font();
852 // currentFont.setPointSize(fontBasePointSize);
854 hovering = false;
855 }
856
857 void
858 TransitionLabel::paint(QPainter* painter,
859 const QStyleOptionGraphicsItem* option,
860 QWidget* widget)
861 {
862 if (parentItem())
863 {
864 StateItem* parentState = qgraphicsitem_cast<StateItem*>(parentItem()->parentItem());
865 LevelOfDetail detail = getLevelOfDetailByParent(painter, parentState);
866
867 // only use scale of parent so that all sub elements of parent (states, transitions) are hidden at once and not one after the other
868 if (detail == eHidden)
869 {
870 return;
871 }
872 }
873 // float lod = QStyleOptionGraphicsItem::levelOfDetailFromTransform(painter->worldTransform());
874 // ARMARX_INFO_S << text().toStdString() << " LOD: " << lod << VAROUT(hovering);
875 // QFont currentFont = font();
876 // if (hovering)
877 // {
878 // float fontSize = currentFont.pointSizeF() * 2;
879 // if (fontSize * lod > fontMaxSize)
880 // {
881 // fontSize = fontMaxSize;
882 // }
883 // currentFont.setPointSizeF(fontSize);
884 // }
885 // else
886 // {
887 // currentFont.setPointSizeF(fontBasePointSize);
888 // }
889 // setFont(currentFont);
890 QGraphicsSimpleTextItem::paint(painter, option, widget);
891 }
892
893 qreal
895 {
896 return font().pointSizeF();
897 }
898
899 qreal
904
905 void
907 {
908 fontBasePointSize = newFontSize;
910 }
911
912 void
914 {
915 QFont currentFont = font();
916 currentFont.setPointSizeF(newFontSize);
917 setFont(currentFont);
918 }
919
920 void
922 {
923 fontEnlargedPointSize = std::max(newFontSize, fontBasePointSize);
924 }
925
926 void
928 {
929 animation.stop();
930 animation.setDuration(200);
931 if (hovering)
932 {
933 animation.setKeyValueAt(0, fontPointSize());
934 animation.setKeyValueAt(1, fontEnlargedPointSize);
935 }
936 else
937 {
938 animation.setKeyValueAt(0, fontPointSize());
939 animation.setKeyValueAt(1, fontBasePointSize);
940 }
941
942 animation.setEasingCurve(QEasingCurve::InOutQuad);
943 animation.start();
944 this->hovering = hovering;
945 }
946
947 bool
949 {
950 return hovering;
951 }
952
953} // namespace armarx
#define option(type, fn)
#define M_PI
Definition MathTools.h:17
#define VAROUT(x)
#define INITIAL_CIRCLE_RADIUS
static QGraphicsView * getView(QGraphicsSceneEvent *event)
MorphingItem(QGraphicsItem *derivedItem)
virtual LevelOfDetail getLevelOfDetailByParent(QPainter *painter, MorphingItem *parent) const
static QPainterPath simplePath(QPointList simpleControlPoints)
simplePath Constructs a cubic Beziercurve with an arbitrary number of control points.
QPainterPath shape() const override
void highlightAnimation(int duration=5000)
void setHighlightColor(const QColor &color)
void attachToMouse(int supportPointIndex)
QPolygonF calcHull(QPainter *painter=0) const
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override
QColor getHighlightColor() const
TransitionItem(statechartmodel::TransitionCPtr transition, QGraphicsItem *parent=0)
statechartmodel::TransitionCPtr getTransition() const
void setEditable(bool editable)
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override
void transitionContextMenuRequested(statechartmodel::TransitionCPtr transition, QPoint mouseScreenPos, QPointF mouseItemPos)
IceUtil::Time getLastHighlightTimestamp() const
void setColor(const QColor &color)
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override
void transitionEndPlaced(statechartmodel::TransitionCPtr transition, QPointF dropPosition)
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override
void mousePressEvent(QGraphicsSceneMouseEvent *event) override
QRectF boundingRect() const override
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
void setSelected(bool selected)
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override
QPropertyAnimation animation
void setEnlargedFontPointSize(qreal newFontSize)
void setHovering(bool hovering)
void setBaseFontPointSize(qreal newFontSize)
void setFontPointSize(qreal newFontSize)
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
TransitionLabel(QString labelText, QGraphicsItem *parent=0)
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
VectorXD< D, T > sqrt(const VectorXD< D, T > &a)
Definition VectorXD.h:704
std::shared_ptr< StateInstance > StateInstancePtr
std::shared_ptr< const Transition > TransitionCPtr
Definition Transition.h:91
This file offers overloads of toIce() and fromIce() functions for STL container types.
double angle(const Point &a, const Point &b, const Point &c)
Definition point.hpp:109
QPointList controlPoints
Definition Transition.h:54
QList< QPointF > toPointList() const