Developer Documentation
connection.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 
43 
44 //== INCLUDES =================================================================
45 #include <QGraphicsSceneMouseEvent>
46 #include <QGraphicsScene>
47 
48 #include "elementInOut.hh"
49 #include "elementInput.hh"
50 #include "elementOutput.hh"
51 #include "connection.hh"
52 #include "connectionPoint.hh"
53 #include "graphicsScene.hh"
54 #include "elementArea.hh"
55 #include "wayfind.hh"
56 
57 //== NAMESPACES ===============================================================
58 namespace VSI {
59 
60 //=============================================================================
61 //
62 // CLASS VSI::Connection - IMPLEMENTATION
63 //
64 //=============================================================================
65 
67 Connection::Connection (ConnectionPoint *_start, QGraphicsScene *_scene) :
68  QGraphicsPathItem (dynamic_cast<GraphicsScene *>(_scene)->elementArea ()),
69  scene_ (dynamic_cast<GraphicsScene *>(_scene)),
70  p1_ (_start),
71  p2_ (0),
72  old_ (0)
73 {
74 
75  elementArea_ = scene_->elementArea ();
76 
77  QPen p = pen ();
78 
79  p.setCapStyle (Qt::RoundCap);
80  p.setColor (QColor (128, 128, 128));
81 
82  if (p1_->inOut ()->inOut ()->typeString () == "data")
83  p.setWidth (4);
84  else
85  p.setWidth (2);
86  setPen (p);
87 }
88 
90 Connection::Connection (ConnectionPoint *_start, ConnectionPoint *_end, QGraphicsScene *_scene) :
91  QGraphicsPathItem (dynamic_cast<GraphicsScene *>(_scene)->elementArea ()),
92  scene_ (dynamic_cast<GraphicsScene *>(_scene)),
93  p1_ (_start),
94  p2_ (_end)
95 {
96  elementArea_ = scene_->elementArea ();
97 
98  QPen p = pen ();
99 
100  p.setCapStyle (Qt::RoundCap);
101 
102  if (p1_->inOut ()->inOut ()->typeString () == "data")
103  p.setWidth (4);
104  else
105  p.setWidth (2);
106  setPen (p);
107 
108  if (!p1_->inOut ()->validConnection (p2_->inOut ()))
109  {
110  deleteLater ();
111  p1_ = p2_ = 0;
112  return;
113  }
114 
115  way_ = QPolygonF ();
116 
117  p.setColor (QColor (0, 0, 0));
118  setPen (p);
119 
120  p1_->inOut ()->addConnection (this);
121  p2_->inOut ()->addConnection (this);
122 }
123 
126 {
127  if (p1_)
128  p1_->inOut ()->removeConnection (this);
129  if (p2_)
130  p2_->inOut ()->removeConnection (this);
131 }
132 
133 // handle mouse movement
134 void Connection::mouseMoveEvent (QGraphicsSceneMouseEvent *_event)
135 {
136  QPen p = pen ();
137 
138  ConnectionPoint *pnt = cPointAt (_event->scenePos ());
139 
140  if (pnt)
141  { if (p1_->inOut ()->validConnection (pnt->inOut ()))
142  p.setColor (QColor (0, 255, 0));
143  else
144  p.setColor (QColor (255, 0, 0));
145  }
146  else
147  p.setColor (QColor (128, 128, 128));
148  setPen (p);
149 
150  way_ = scene_->wayFind ()->findWay (this, p1_->connectPos ().toPoint (), elementArea_->mapFromScene (_event->scenePos ()).toPoint ());
151  updateLine ();
152 }
153 
154 // Start new connection on mouse press
155 void Connection::mousePressEvent (QGraphicsSceneMouseEvent *_event)
156 {
157  if (p1_)
158  p1_->inOut ()->removeConnection (this);
159  if (p2_)
160  p2_->inOut ()->removeConnection (this);
161 
162  QPointF p = p1_->connectPos () - _event->scenePos ();
163  qreal d1 = (p.x () * p.x()) + (p.y () * p.y ());
164  p = p2_->connectPos () - _event->scenePos ();
165  qreal d2 = (p.x () * p.x()) + (p.y () * p.y ());
166 
167  if (d2 > d1)
168  {
169  old_ = p1_;
170  p1_ = p2_;
171  }
172 
173  way_ = scene_->wayFind ()->findWay (this, p1_->connectPos ().toPoint (), elementArea_->mapFromScene (_event->scenePos ()).toPoint ());
174  updateLine ();
175 }
176 
177 // make a connection on relase at a valid connection point
178 void Connection::mouseReleaseEvent (QGraphicsSceneMouseEvent *_event)
179 {
180  scene_->setActiveConnection (0);
181 
182  p2_ = cPointAt (_event->scenePos ());
183 
184  if (!p2_ || !p1_->inOut ()->validConnection (p2_->inOut ()))
185  {
186  if (old_)
187  scene_->contentChange ();
188 
189  deleteLater ();
190  p1_ = p2_ = 0;
191  return;
192  }
193 
194  way_ = scene_->wayFind ()->findWay (this, p1_->connectPos ().toPoint (), p2_->connectPos ().toPoint ());
195  updateLine ();
196 
197  QPen p = pen ();
198 
199  p.setColor (QColor (0, 0, 0));
200  setPen (p);
201 
202  if (old_ != p2_)
203  scene_->contentChange ();
204 
205  old_ = p2_;
206 
207  p1_->inOut ()->addConnection (this);
208  p2_->inOut ()->addConnection (this);
209 
210 }
211 
212 // helper to localize a connection point
213 ConnectionPoint *Connection::cPointAt (QPointF _pnt)
214 {
215 
216  ConnectionPoint *pnt = 0;
217 
218  foreach (QGraphicsItem *i, scene ()->items (_pnt))
219  {
220  ConnectionPoint *pt = dynamic_cast<ConnectionPoint *>(i);
221 
222  if (pt)
223  {
224  pnt = pt;
225  }
226  }
227 
228  return pnt;
229 }
230 
233 {
234  way_ = scene_->wayFind ()->findWay (this, p1_->connectPos ().toPoint (), p2_->connectPos ().toPoint ());
235  updateLine ();
236 }
237 
240 {
241  ElementInput *i = dynamic_cast<ElementInput *> (p1_->inOut ());
242  if (!i)
243  i = dynamic_cast<ElementInput *> (p2_->inOut ());
244  return i;
245 }
246 
249 {
250  ElementOutput *o = dynamic_cast<ElementOutput *> (p1_->inOut ());
251  if (!o && p2_)
252  o = dynamic_cast<ElementOutput *> (p2_->inOut ());
253  return o;
254 }
255 
257 const QPolygonF & VSI::Connection::way () const
258 {
259  return way_;
260 }
261 
264 {
265  way_ = QPolygonF ();
266  setPath (QPainterPath ());
267 }
268 
269 // generates a curved line out of the way polygon
270 void Connection::updateLine()
271 {
272  QPainterPath path;
273 
274  if (way_.size () <= 1)
275  {
276  setPath (path);
277  return;
278  }
279  else if (way_.size () == 2)
280  {
281  path.addPolygon(way_);
282  setPath (path);
283  return;
284  }
285 
286  path.moveTo (way_[0]);
287 
288  QPointF a,b,c;
289 
290  for (int i = 2; i < way_.size (); i++)
291  {
292  a = way_[i-2];
293  b = way_[i-1];
294  c = way_[i];
295 
296  QLineF l1 (a,b), l2 (b,c);
297 
298  int rad = qMin (40.0, qMin (l1.length() / 2, l2.length() / 2));
299 
300  if (rad > 5)
301  rad -= rad % 5;
302 
303  if (a.x () == b.x ())
304  {
305  if (a.y () > b.y ())
306  {
307  path.lineTo (b.x (), b.y () + rad);
308  if (c.x () > b.x ())
309  {
310  path.arcTo (b.x (), b.y (), 2 * rad, 2 * rad, 180, -90);
311  }
312  else
313  {
314  path.arcTo (b.x () - (rad * 2), b.y (), 2 * rad, 2 * rad, 0, 90);
315  }
316  }
317  else
318  {
319  path.lineTo (b.x (), b.y () - rad);
320  if (c.x () > b.x ())
321  {
322  path.arcTo (b.x (), b.y () - (rad * 2), 2 * rad, 2 * rad, 180, 90);
323  }
324  else
325  {
326  path.arcTo (b.x () - (rad * 2), b.y () - (rad * 2), 2 * rad, 2 * rad, 0, -90);
327  }
328  }
329  }
330  else
331  {
332  if (a.x () > b.x ())
333  {
334  path.lineTo (b.x () + rad, b.y ());
335  if (c.y () > b.y ())
336  {
337  path.arcTo (b.x (), b.y (), 2 * rad, 2 * rad, 90, 90);
338  }
339  else
340  {
341  path.arcTo (b.x (), b.y () - (rad * 2), 2 * rad, 2 * rad, 270, -90);
342  }
343  }
344  else
345  {
346  path.lineTo (b.x () - rad, b.y ());
347  if (c.y () > b.y ())
348  {
349  path.arcTo (b.x () - (rad * 2), b.y (), 2 * rad, 2 * rad, 90, -90);
350  }
351  else
352  {
353  path.arcTo (b.x () - (rad * 2), b.y () - (rad * 2), 2 * rad, 2 * rad, 270, 90);
354  }
355  }
356  }
357  }
358 
359  path.lineTo (c);
360 
361  setPath (path);
362 }
363 
364 }
InOut * inOut() const
InOut context object.
QPolygonF findWay(Connection *_conn, QPoint _from, QPoint _to)
Finds a way from _from to _to ignoring any already existent connections from _conn.
Definition: wayfind.cc:95
virtual void addConnection(Connection *_conn)
Add the connection.
QPointF connectPos()
Position for connections.
~Connection()
Destructor.
Definition: connection.cc:125
void contentChange()
handle content changes
QString typeString() const
Type.
Definition: inout.cc:65
void updatePositions()
called to update position on element movement
Definition: connection.cc:232
ElementArea * elementArea() const
Element area.
void setActiveConnection(Connection *_conn)
Sets the active connection.
const QPolygonF & way() const
way of the connection
Definition: connection.cc:257
void invalidate()
invalidate way
Definition: connection.cc:263
virtual void removeConnection(Connection *_conn)
Remove the Connection.
ElementOutput * output()
Output of this connection.
Definition: connection.cc:248
bool validConnection(ElementInOut *_e)
Can this input/output be connected to _e.
ElementInput * input()
Input of this connection.
Definition: connection.cc:239
ElementInOut * inOut() const
Input/output element.
Connection(ConnectionPoint *_start, QGraphicsScene *_scene)
Constructor.
Definition: connection.cc:67
WayFind * wayFind()
WayFind object.