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