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
42  QGraphicsPathItem(0, 0), m_startControlPoint(), m_endControlPoint()
43 {
44 }
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),
58  m_startControlPoint(),
59  m_endControlPoint(),
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 
155 void
157 {
158  m_transitionName = name;
159  m_pTransitionLabel->setText(name);
160 }
161 
162 void
163 StateChartGraphEdge::setLineTo(QPointF newEndPoint)
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 
208 void
209 StateChartGraphEdge::setLineFrom(QPointF newStartPoint)
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 
252 void
253 StateChartGraphEdge::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 
260  if (!m_bInfoBoxUnfolded)
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 
288 void
289 StateChartGraphEdge::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 
313 float
314 StateChartGraphEdge::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 
321 float
322 StateChartGraphEdge::calcLengthOfLine(QPointF start, QPointF end)
323 {
324  return calcLengthOfLine(floatPair(start.x(), start.y()), floatPair(end.x(), end.y()));
325 }
326 
327 std::vector<floatPair>
328 StateChartGraphEdge::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 
400 void
401 StateChartGraphEdge::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 
426 QPointF
428 {
429  QPointF endPoint(m_endControlPoint.first, m_endControlPoint.second);
430  return endPoint;
431 }
432 
433 QPointF
435 {
436  QPointF startPoint(m_startControlPoint.first, m_startControlPoint.second);
437  return startPoint;
438 }
439 
442 {
443  return *this;
444 }
445 
446 void
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 
473 void
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 
500 bool
502 {
503  bool infoBoxClicked = m_pTransitionInfoPoint->contains(point);
504 
505  if (infoBoxClicked)
506  {
507  if (!m_bInfoBoxUnfolded)
508  {
509  unfoldInfoBox();
510  }
511  else
512  {
513  foldInfoBox();
514  }
515  }
516 
517  return infoBoxClicked;
518 }
StateChartGraphEdge
Definition: Transition.h:45
StateChartGraphEdge::m_endControlPoint
floatPair m_endControlPoint
Definition: Transition.h:102
StateChartGraphEdge::m_bInfoBoxUnfolded
bool m_bInfoBoxUnfolded
Definition: Transition.h:104
StateChartGraphEdge::StateChartGraphEdge
StateChartGraphEdge(const StateChartGraphEdge &)
Definition: Transition.cpp:41
StateChartGraphEdge::m_Path
QPainterPath m_Path
Definition: Transition.h:85
StateChartGraphEdge::unfoldInfoBox
void unfoldInfoBox()
Definition: Transition.cpp:447
StateChartGraphEdge::~StateChartGraphEdge
~StateChartGraphEdge()
Definition: Transition.cpp:145
pi
#define pi
Definition: Transition.cpp:38
StateChartGraphEdge::setTransitionName
void setTransitionName(const QString &name)
Definition: Transition.cpp:156
floatPair
std::pair< float, float > floatPair
Definition: Transition.h:43
StateChartGraphEdge::getStartPoint
QPointF getStartPoint()
Definition: Transition.cpp:434
GfxTL::sqrt
VectorXD< D, T > sqrt(const VectorXD< D, T > &a)
Definition: VectorXD.h:704
StateChartGraphEdge::setLineTo
void setLineTo(QPointF newEndPoint)
Definition: Transition.cpp:163
StateChartGraphEdge::infoBoxClicked
bool infoBoxClicked(const QPointF &point)
Definition: Transition.cpp:501
Logging.h
StateChartGraphEdge::getEndPoint
QPointF getEndPoint()
Definition: Transition.cpp:427
StateChartGraphEdge::m_startControlPoint
floatPair m_startControlPoint
Definition: Transition.h:98
StateChartGraphEdge::setLineFrom
void setLineFrom(QPointF newStartPoint)
Definition: Transition.cpp:209
StateChartGraphEdge::operator=
StateChartGraphEdge & operator=(const StateChartGraphEdge &)
Definition: Transition.cpp:441
StateChartGraphEdge::foldInfoBox
void foldInfoBox()
Definition: Transition.cpp:474