Transition.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 <math.h>
25
26#include <string>
27#include <utility>
28
29#include <boost/lexical_cast.hpp>
30
31#include <QGraphicsPolygonItem>
32#include <QPolygonF>
33
35
36#include "StateChartGraphEdge.h"
37
38#define pi 3.14159265
39
40//TODO: implement as copy constructor
45
47 QGraphicsPathItem(0, scene), m_startControlPoint(), m_endControlPoint()
48{
49}
50
52 const QString& transitionName,
53 QGraphicsScene* scene,
54 float dpi) :
55 QGraphicsPathItem(0, scene),
56 m_headNode(gvGraphEdge->head->name),
57 m_tailNode(gvGraphEdge->tail->name),
60 m_bInfoBoxUnfolded(false),
61 m_transitionName(transitionName)
62{
63 m_InfoPointRadius = dpi / 6;
64 m_ArrowScale = dpi / 10;
65
66 std::vector<floatPair> controlPoints = calcControlPoints(gvGraphEdge);
67 calcPath(controlPoints);
68
69 //------------------------------------------------------------------------
70
71 //set the path's color, width and arrowhead
72 QPen pen;
73 pen.setWidthF(1.5);
74 pen.setColor(Qt::black);
75 this->setPen(pen);
76 this->setPath(m_Path);
77 m_Arrow = new QGraphicsPolygonItem(this, scene);
78 m_Arrow->setBrush(QBrush(Qt::black));
79 //calculate the arrowhead's position on basis of second last and last control point
80 setArrowHeadPosition(QPointF(controlPoints[int(controlPoints.size()) - 2].first,
81 controlPoints[int(controlPoints.size()) - 2].second),
82 QPointF(m_endControlPoint.first, m_endControlPoint.second));
83
84 //-------------------------------------------------------------------------
85
86 //set the transition label
87 m_pTransitionLabel = new QGraphicsSimpleTextItem(transitionName, this);
88 QRectF transitionLabelRect = m_pTransitionLabel->boundingRect();
89 float transitionLength = calcLengthOfLine(m_startControlPoint, m_endControlPoint);
90
91 //angle between x direction and edge
92 float labelAngle =
93 acos((m_endControlPoint.first - m_startControlPoint.first) / transitionLength);
94 int arg = 1;
95
96 if (m_endControlPoint.second - m_startControlPoint.second < 0)
97 {
98 labelAngle = -labelAngle; //angle is negative if end is lower than start
99 }
100
101 //adjust angle if end is to the left of start
102 if (m_endControlPoint.first - m_startControlPoint.first < 0)
103 {
104 labelAngle = pi + labelAngle;
105 arg = -1;
106 }
107
108 //set position and angle of the transition label
109 m_pTransitionLabel->setPos(
110 m_startControlPoint.first +
111 (0.5 * (transitionLength - arg * transitionLabelRect.width()) *
112 (m_endControlPoint.first - m_startControlPoint.first) / transitionLength),
113 m_startControlPoint.second +
114 (0.5 * (transitionLength - arg * transitionLabelRect.width()) *
115 (m_endControlPoint.second - m_startControlPoint.second) / transitionLength));
116 m_pTransitionLabel->setPos(
117 m_Path.pointAtPercent(0.5).x() - arg * transitionLabelRect.width() * 0.5 *
118 (m_endControlPoint.first - m_startControlPoint.first) /
119 transitionLength,
120 m_Path.pointAtPercent(0.5).y() -
121 arg * transitionLabelRect.width() * 0.5 *
122 (m_endControlPoint.second - m_startControlPoint.second) / transitionLength);
123 m_pTransitionLabel->rotate(labelAngle * 180 / pi);
124
125 setInfoPointPosition(
126 QPointF(m_pTransitionLabel->pos().x() + transitionLabelRect.width() * cos(labelAngle),
127 m_pTransitionLabel->pos().y() + transitionLabelRect.width() * sin(labelAngle)),
128 labelAngle);
129
130 //-----------------------------------------------------------------------------
131
132 m_pTransitionInfoPoint = new QGraphicsEllipseItem(
133 this, scene); //how can an ellipse be created from a QGraphicsPathItem (this) ?
134 m_Cross = new QGraphicsPathItem(m_pTransitionInfoPoint, scene);
135
136 //create info box and text
137 m_InfoBox = new QGraphicsRectItem(this);
138 m_InfoBox->setBrush(QBrush(Qt::white));
139 m_InfoText = new QGraphicsSimpleTextItem(QString("test1\ntest2"), m_InfoBox);
140
141 m_InfoText->hide();
142 m_InfoBox->hide();
143}
144
146{
147 delete m_Arrow;
148 delete m_Cross;
149 delete m_InfoText;
150 delete m_InfoBox;
151 delete m_pTransitionLabel;
152 delete m_pTransitionInfoPoint;
153}
154
155void
157{
158 m_transitionName = name;
159 m_pTransitionLabel->setText(name);
160}
161
162void
164{
165 QPointF startPoint(m_startControlPoint.first, m_startControlPoint.second);
166 m_Path = QPainterPath(startPoint);
167 m_Path.lineTo(newEndPoint);
168 m_endControlPoint.first = newEndPoint.x();
169 m_endControlPoint.second = newEndPoint.y();
170
171 setArrowHeadPosition(QPointF(m_startControlPoint.first, m_startControlPoint.second),
172 QPointF(m_endControlPoint.first, m_endControlPoint.second));
173 QRectF transitionRect = m_pTransitionLabel->boundingRect();
174 m_pTransitionLabel->resetTransform();
175 float transitionLength = sqrt(pow(m_endControlPoint.first - m_startControlPoint.first, 2) +
176 pow(m_endControlPoint.second - m_startControlPoint.second, 2));
177 float labelAngle =
178 acos((m_endControlPoint.first - m_startControlPoint.first) / transitionLength);
179 int arg = 1;
180
181 if (m_endControlPoint.second - m_startControlPoint.second < 0)
182 {
183 labelAngle = -labelAngle;
184 }
185
186 if (m_endControlPoint.first - m_startControlPoint.first < 0)
187 {
188 labelAngle = pi + labelAngle;
189 arg = -1;
190 }
191
192 m_pTransitionLabel->setPos(
193 m_startControlPoint.first +
194 (0.5 * (transitionLength - arg * transitionRect.width()) *
195 (m_endControlPoint.first - m_startControlPoint.first) / transitionLength),
196 m_startControlPoint.second +
197 (0.5 * (transitionLength - arg * transitionRect.width()) *
198 (m_endControlPoint.second - m_startControlPoint.second) / transitionLength));
199 m_pTransitionLabel->rotate(labelAngle * 180 / pi);
200
201 setInfoPointPosition(
202 QPointF(m_pTransitionLabel->pos().x() + transitionRect.width() * cos(labelAngle),
203 m_pTransitionLabel->pos().y() + transitionRect.width() * sin(labelAngle)),
204 labelAngle);
205 this->setPath(m_Path);
206}
207
208void
210{
211 QPointF endPoint(m_endControlPoint.first, m_endControlPoint.second);
212 m_Path = QPainterPath(newStartPoint);
213 m_Path.lineTo(endPoint);
214 m_startControlPoint.first = newStartPoint.x();
215 m_startControlPoint.second = newStartPoint.y();
216 setArrowHeadPosition(QPointF(m_startControlPoint.first, m_startControlPoint.second),
217 QPointF(m_endControlPoint.first, m_endControlPoint.second));
218 QRectF transitionRect = m_pTransitionLabel->boundingRect();
219 m_pTransitionLabel->resetTransform();
220 float transitionLength = sqrt(pow(m_endControlPoint.first - m_startControlPoint.first, 2) +
221 pow(m_endControlPoint.second - m_startControlPoint.second, 2));
222 float labelAngle =
223 acos((m_endControlPoint.first - m_startControlPoint.first) / transitionLength);
224 int arg = 1;
225
226 if (m_endControlPoint.second - m_startControlPoint.second < 0)
227 {
228 labelAngle = -labelAngle;
229 }
230
231 if (m_endControlPoint.first - m_startControlPoint.first < 0)
232 {
233 labelAngle = pi + labelAngle;
234 arg = -1;
235 }
236
237 m_pTransitionLabel->setPos(
238 m_startControlPoint.first +
239 (0.5 * (transitionLength - arg * transitionRect.width()) *
240 (m_endControlPoint.first - m_startControlPoint.first) / transitionLength),
241 m_startControlPoint.second +
242 (0.5 * (transitionLength - arg * transitionRect.width()) *
243 (m_endControlPoint.second - m_startControlPoint.second) / transitionLength));
244 m_pTransitionLabel->rotate(labelAngle * 180 / pi);
245 setInfoPointPosition(
246 QPointF(m_pTransitionLabel->pos().x() + transitionRect.width() * cos(labelAngle),
247 m_pTransitionLabel->pos().y() + transitionRect.width() * sin(labelAngle)),
248 labelAngle);
249 this->setPath(m_Path);
250}
251
252void
253StateChartGraphEdge::setInfoPointPosition(QPointF newPosition, double labelAngle)
254{
255 QPointF startPoint(0, -m_InfoPointRadius / 3);
256 QPointF endPoint(0, m_InfoPointRadius / 3);
257 QPointF startPoint2(-m_InfoPointRadius / 3, 0);
258 QPointF endPoint2(m_InfoPointRadius / 3, 0);
259
261 {
262 m_CrossPainter = QPainterPath(startPoint);
263 m_CrossPainter.lineTo(endPoint);
264 m_CrossPainter.moveTo(startPoint2);
265 m_CrossPainter.lineTo(endPoint2);
266 m_Cross->setPath(m_CrossPainter);
267 }
268 else
269 {
270 m_CrossPainter = QPainterPath(startPoint2);
271 m_CrossPainter.lineTo(endPoint2);
272 m_Cross->setPath(m_CrossPainter);
273 }
274
275 m_pTransitionInfoPoint->setRect(
276 newPosition.x(), newPosition.y() - m_InfoPointRadius, m_InfoPointRadius, m_InfoPointRadius);
277 m_Cross->resetTransform();
278 m_Cross->setPos(newPosition.x() + m_InfoPointRadius / 2,
279 newPosition.y() - m_InfoPointRadius / 2);
280 m_Cross->rotate(labelAngle * 180 / pi);
281 m_InfoText->setPos(newPosition.x() + m_InfoPointRadius, newPosition.y() - m_InfoPointRadius);
282 m_InfoBox->setRect(newPosition.x() + m_InfoPointRadius,
283 newPosition.y() - m_InfoPointRadius,
284 m_InfoText->boundingRect().width(),
285 m_InfoText->boundingRect().height());
286}
287
288void
289StateChartGraphEdge::setArrowHeadPosition(QPointF headLineSegmentStart, QPointF headLineSegmentEnd)
290{
291 QPolygonF arrowHead;
292 //vector between headLineSegmentStart and headLineSegmentEnd
293 QPointF arrowVector = headLineSegmentEnd - headLineSegmentStart;
294 //calculate length of arrowVector
295 double lengthLastSegment = calcLengthOfLine(headLineSegmentStart, headLineSegmentStart);
296 arrowVector.setX(arrowVector.x() / lengthLastSegment);
297 arrowVector.setY(arrowVector.y() / lengthLastSegment);
298
299 //arrowHead = polygon from headLineSegmentEnd(arrow point ?) to lower "wing" end point to upper "wing" end point
300 //why not different order: lower wing - arrow point - upper wing ?
301 arrowHead << headLineSegmentEnd;
302 arrowHead << QPointF(
303 headLineSegmentEnd.x() - m_ArrowScale * (arrowVector.x() - arrowVector.y()),
304 headLineSegmentEnd.y() - m_ArrowScale * (arrowVector.y() + arrowVector.x()));
305 arrowHead << QPointF(
306 headLineSegmentEnd.x() - m_ArrowScale * (arrowVector.x() + arrowVector.y()),
307 headLineSegmentEnd.y() - m_ArrowScale * (arrowVector.y() - arrowVector.x()));
308
309 //set m_Arrow to arrowHead
310 m_Arrow->setPolygon(arrowHead);
311}
312
313float
314StateChartGraphEdge::calcLengthOfLine(floatPair start, floatPair end)
315{
316 float xDiff = end.first - start.first;
317 float yDiff = end.second - start.second;
318 return sqrt(pow(xDiff, 2) + pow(yDiff, 2));
319}
320
321float
322StateChartGraphEdge::calcLengthOfLine(QPointF start, QPointF end)
323{
324 return calcLengthOfLine(floatPair(start.x(), start.y()), floatPair(end.x(), end.y()));
325}
326
327std::vector<floatPair>
328StateChartGraphEdge::calcControlPoints(Agedge_t* gvGraphEdge)
329{
330 std::vector<floatPair> controlPoints;
331
332 //get position of gvGraphEdge as string
333 std::string position(agget(gvGraphEdge, const_cast<char*>("pos")));
334 size_t oldDividerPos;
335 size_t dividerPos;
336
337 //dispose of part before first comma
338 dividerPos = position.find_first_of(",");
339 assert(dividerPos != std::string::npos); //there has to be a comma
340 oldDividerPos = dividerPos;
341 // ARMARX_IMPORTANT_S << transitionName.toStdString() << " pos: " << strangePos;
342
343 //set x position of endControlPoint to the part of position between first and second comma
344 dividerPos = position.find_first_of(",", oldDividerPos + 1);
345 assert(dividerPos != std::string::npos); //there has to be a comma
346 std::string stringEndX = position.substr(oldDividerPos + 1, dividerPos);
347 oldDividerPos = dividerPos;
348
349 m_endControlPoint.first = boost::lexical_cast<float>(stringEndX);
350
351 //set y position of endControlPoint to the part of position between second and third comma
352 dividerPos = position.find_first_of(",", oldDividerPos + 1);
353 assert(dividerPos != std::string::npos); //there has to be a comma
354 std::string stringEndY = position.substr(oldDividerPos + 1, dividerPos);
355 oldDividerPos = dividerPos;
356
357 m_endControlPoint.second = boost::lexical_cast<float>(stringEndY);
358
359 //set x position of startControlPoint to the part of position between third and fourth comma
360 dividerPos = position.find_first_of(",", oldDividerPos + 1);
361 assert(dividerPos != std::string::npos); //there has to be a comma
362 std::string stringStartX = position.substr(oldDividerPos + 1, dividerPos);
363 oldDividerPos = dividerPos;
364
365 m_startControlPoint.first = boost::lexical_cast<float>(stringStartX);
366
367 //set y position of startControlPoint to the part of position between third and fourth comma
368 dividerPos = position.find_first_of(",", oldDividerPos + 1);
369 std::string stringStartY = position.substr(oldDividerPos + 1, dividerPos);
370 oldDividerPos = dividerPos;
371
372 m_startControlPoint.second = boost::lexical_cast<float>(stringStartY);
373 controlPoints.push_back(m_startControlPoint);
374
375 //calculate inner control points
376 while (oldDividerPos != std::string::npos)
377 {
378 floatPair controlPoint;
379
380 dividerPos = position.find_first_of(",", oldDividerPos + 1);
381 assert(dividerPos !=
382 std::string::npos); //there has to be a comma because y position needs to follow
383 std::string posX = position.substr(oldDividerPos + 1, dividerPos);
384 oldDividerPos = dividerPos;
385
386 dividerPos = position.find_first_of(",", oldDividerPos + 1);
387 std::string posY = position.substr(oldDividerPos + 1, dividerPos);
388 oldDividerPos = dividerPos;
389
390 controlPoint.first = boost::lexical_cast<float>(posX);
391 controlPoint.second = boost::lexical_cast<float>(posY);
392 controlPoints.push_back(controlPoint);
393 }
394
395 controlPoints.push_back(m_endControlPoint);
396
397 return controlPoints;
398}
399
400void
401StateChartGraphEdge::calcPath(std::vector<floatPair> controlPoints)
402{
403 //construct the path based on the controlPoints
404 m_Path.moveTo(controlPoints[0].first, controlPoints[0].second);
405
406 if (int(controlPoints.size()) > 2)
407 {
408 //join all controlpoints by quadratic Bezier curves
409 for (int i = 1; i < int(controlPoints.size()) - 1; i += 1)
410 {
411 m_Path.quadTo(controlPoints[i].first,
412 controlPoints[i].second,
413 controlPoints[i + 1].first,
414 controlPoints[i + 1].second);
415 // m_Path.cubicTo(controlPoints[i].first,controlPoints[i].second,controlPoints[i+1].first,controlPoints[i+1].second,controlPoints[i+2].first,controlPoints[i+2].second);
416 }
417 }
418 else
419 {
420 //path = line from start to end point
421 m_Path.lineTo(controlPoints[controlPoints.size() - 1].first,
422 controlPoints[controlPoints.size() - 1].second);
423 }
424}
425
426QPointF
428{
429 QPointF endPoint(m_endControlPoint.first, m_endControlPoint.second);
430 return endPoint;
431}
432
433QPointF
435{
436 QPointF startPoint(m_startControlPoint.first, m_startControlPoint.second);
437 return startPoint;
438}
439
442{
443 return *this;
444}
445
446void
448{
449 m_bInfoBoxUnfolded = true;
450 float labelAngle = acos((m_endControlPoint.first - m_startControlPoint.first) /
451 sqrt(pow(m_endControlPoint.first - m_startControlPoint.first, 2) +
452 pow(m_endControlPoint.second - m_startControlPoint.second, 2)));
453
454 if (m_endControlPoint.second - m_startControlPoint.second < 0)
455 {
456 labelAngle = -labelAngle;
457 }
458
459 if (m_endControlPoint.first - m_startControlPoint.first < 0)
460 {
461 labelAngle = pi + labelAngle;
462 }
463
464 QRectF transitionRect = m_pTransitionLabel->boundingRect();
465 setInfoPointPosition(
466 QPointF(m_pTransitionLabel->pos().x() + transitionRect.width() * cos(labelAngle),
467 m_pTransitionLabel->pos().y() + transitionRect.width() * sin(labelAngle)),
468 labelAngle);
469 m_InfoText->show();
470 m_InfoBox->show();
471}
472
473void
475{
476 m_bInfoBoxUnfolded = false;
477 float labelAngle = acos((m_endControlPoint.first - m_startControlPoint.first) /
478 sqrt(pow(m_endControlPoint.first - m_startControlPoint.first, 2) +
479 pow(m_endControlPoint.second - m_startControlPoint.second, 2)));
480
481 if (m_endControlPoint.second - m_startControlPoint.second < 0)
482 {
483 labelAngle = -labelAngle;
484 }
485
486 if (m_endControlPoint.first - m_startControlPoint.first < 0)
487 {
488 labelAngle = pi + labelAngle;
489 }
490
491 QRectF transitionRect = m_pTransitionLabel->boundingRect();
492 setInfoPointPosition(
493 QPointF(m_pTransitionLabel->pos().x() + transitionRect.width() * cos(labelAngle),
494 m_pTransitionLabel->pos().y() + transitionRect.width() * sin(labelAngle)),
495 labelAngle);
496 m_InfoText->hide();
497 m_InfoBox->hide();
498}
499
500bool
502{
503 bool infoBoxClicked = m_pTransitionInfoPoint->contains(point);
504
505 if (infoBoxClicked)
506 {
508 {
510 }
511 else
512 {
513 foldInfoBox();
514 }
515 }
516
517 return infoBoxClicked;
518}
#define pi
std::pair< float, float > floatPair
Definition Transition.h:43
void setLineTo(QPointF newEndPoint)
void setLineFrom(QPointF newStartPoint)
StateChartGraphEdge & operator=(const StateChartGraphEdge &)
StateChartGraphEdge(const StateChartGraphEdge &)
void setTransitionName(const QString &name)
QPainterPath m_Path
Definition Transition.h:85
bool infoBoxClicked(const QPointF &point)
floatPair m_startControlPoint
Definition Transition.h:98
floatPair m_endControlPoint
Definition Transition.h:102
VectorXD< D, T > sqrt(const VectorXD< D, T > &a)
Definition VectorXD.h:704