OpenWalnut  1.5.0dev
WQtNetworkItemGrid.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <cmath>
26 
27 #include <algorithm>
28 
29 #include <QPainter>
30 
31 #include "core/common/WLogger.h"
32 
33 #include "WQtNetworkEditorGlobals.h"
34 #include "WQtNetworkItem.h"
35 
36 #include "WQtNetworkItemGrid.h"
37 
40  m_highlightCellEnabled( false ),
41  m_disableUpdateBounds( false )
42 {
43  // initialize members
44  setZValue( -1.0 );
45  setOpacity( 0.0 );
46 
47  // blend timer
48  m_blendInTimer = new QTimeLine( WNETWORKITEM_GRIDBLENDIN_DURATION );
49  connect( m_blendInTimer, SIGNAL( valueChanged( qreal ) ), this, SLOT( animationBlendInTick( qreal ) ) );
50 
51  m_blendOutTimer = new QTimeLine( WNETWORKITEM_GRIDBLENDOUT_DURATION );
52  connect( m_blendOutTimer, SIGNAL( valueChanged( qreal ) ), this, SLOT( animationBlendOutTick( qreal ) ) );
53 }
54 
56 {
57  // cleanup
58  delete m_blendInTimer;
59  delete m_blendOutTimer;
60 }
61 
63 {
64  return m_bb;
65 }
66 
68 {
69  float boxWidth = WNETWORKITEM_XSPACE + WNETWORKITEM_MAXIMUM_WIDTH;
70  float boxHeight = WNETWORKITEM_YSPACE + WNETWORKITEM_MAXIMUM_HEIGHT;
71 
72  QRect dimensions = m_dimensions;
73 
74  // always include hightlight cell to force QGraphicsScene to render it properly
76  {
77  QRect h( m_highlightCell.x(), m_highlightCell.y(), 1, 1 );
78  dimensions = dimensions.united( h );
79  }
80 
81  // NOTE: we always add a row and column at each side for the visual representation of the grid
82  float minX = ( dimensions.x() - 1 ) * boxWidth;
83  float minY = ( dimensions.y() - 1 ) * boxHeight;
84  float maxX = ( dimensions.width() + 2 ) * boxWidth;
85  float maxY = ( dimensions.height() + 2 ) * boxHeight;
86 
87  // done. Create rect and emit signal
88  m_bb = QRectF( minX, minY, maxX, maxY );
89 
91  {
92  emit updatedBounds();
93  }
94 }
95 
96 void WQtNetworkItemGrid::paint( QPainter* painter, const QStyleOptionGraphicsItem* /* option */,
97  QWidget* /* widget */ )
98 {
99  QRectF bb = boundingRect();
100 
101  float sx = WNETWORKITEM_XSPACE + WNETWORKITEM_MAXIMUM_WIDTH;
102  float sy = WNETWORKITEM_YSPACE + WNETWORKITEM_MAXIMUM_HEIGHT;
103 
104  float lineWidth = 1.0;
105 
106  // draw a nice grid
107  for( float y = sy; y < bb.height(); y = y + sy )
108  {
109  QRectF r( bb.x(), bb.y() + y, bb.width(), lineWidth );
110  QLinearGradient gradient( bb.topLeft(), bb.topRight() );
111  gradient.setColorAt( 0.0, QColor( 0, 0, 0, 0 ) );
112  gradient.setColorAt( 0.4, QColor( 0, 0, 0, 128 ) );
113  gradient.setColorAt( 0.6, QColor( 0, 0, 0, 128 ) );
114  gradient.setColorAt( 1.0, QColor( 0, 0, 0, 0 ) );
115  painter->fillRect( r, gradient );
116  }
117 
118  for( float x = sx; x < bb.width(); x = x + sx )
119  {
120  QRectF r( bb.x() + x, bb.y(), lineWidth, bb.height() );
121  QLinearGradient gradient( bb.topLeft(), bb.bottomLeft() );
122  gradient.setColorAt( 0.0, QColor( 0, 0, 0, 0 ) );
123  gradient.setColorAt( 0.4, QColor( 0, 0, 0, 128 ) );
124  gradient.setColorAt( 0.6, QColor( 0, 0, 0, 128 ) );
125  gradient.setColorAt( 1.0, QColor( 0, 0, 0, 0 ) );
126  painter->fillRect( r, gradient );
127  }
128 
130  {
131  QRectF cellRect = mapCellAreaToWorld( m_highlightCell );
132 
133  // draw highlight
134  // QRadialGradient radialGrad( cellRect.center(), cellRect.height() );
135  // radialGrad.setColorAt( 0, m_highlightColor );
136  // radialGrad.setColorAt( 0.2, QColor( 0,0,0,0 ) );
137  // radialGrad.setColorAt( 0.8, QColor( 0,0,0,0 ) );
138  // radialGrad.setColorAt( 1, m_highlightColor );
139  painter->fillRect( cellRect, m_highlightColor );
140  }
141 }
142 
143 void WQtNetworkItemGrid::physicalMoveTo( QGraphicsItem* item, int col, int row, bool animate )
144 {
145  QPointF p = mapToWorld( col, row );
146 
147  p.rx() += WNETWORKITEM_XSPACE * 0.5;
148  p.ry() += WNETWORKITEM_YSPACE * 0.5;
149 
150  WQtNetworkItem* ni = dynamic_cast< WQtNetworkItem* >( item );
151  if( animate && ni )
152  {
153  ni->animatedMoveTo( p );
154  }
155  else if( ni )
156  {
157  ni->moveTo( p );
158  }
159  else
160  {
161  item->setPos( p );
162  }
163 }
164 
165 void WQtNetworkItemGrid::physicalMoveTo( QGraphicsItem* item, QPoint cell, bool animate )
166 {
167  physicalMoveTo( item, cell.x(), cell.y(), animate );
168 }
169 
170 bool WQtNetworkItemGrid::setItem( QGraphicsItem* item, int col, int row )
171 {
172  // already owned?
173  if( isOccupied( col, row ) )
174  {
175  return false;
176  }
177 
178  // insert element into the corresponding position
179  setItemImpl( item, col, row );
180 
181  // move element physically
182  physicalMoveTo( item, col, row, false );
183 
184  return true;
185 }
186 
187 bool WQtNetworkItemGrid::setItem( QGraphicsItem* item, QPoint cell )
188 {
189  return setItem( item, cell.x(), cell.y() );
190 }
191 
192 void WQtNetworkItemGrid::removeItem( int col, int row )
193 {
194  removeItemImpl( m_grid[ QPoint( col, row ) ] );
195 }
196 
198 {
199  return removeItemImpl( item );
200 }
201 
202 bool WQtNetworkItemGrid::moveItem( QGraphicsItem* item, int col, int row )
203 {
204  // already in use?
205  if( isOccupied( col, row ) )
206  {
207  return false;
208  }
209 
210  // clear old entry?
211  removeItemImpl( item, true );
212 
213  // set new
214  setItemImpl( item, col, row, true );
215 
216  // move element physically
217  physicalMoveTo( item, col, row, true );
218 
219  // grid might be larger/smaller now
221 
222  return true;
223 }
224 
225 bool WQtNetworkItemGrid::moveItem( QGraphicsItem* item, QPoint cell )
226 {
227  return moveItem( item, cell.x(), cell.y() );
228 }
229 
230 bool WQtNetworkItemGrid::isOccupied( int col, int row )
231 {
232  return at( col, row );
233 }
234 
236 {
237  return isOccupied( cell.x(), cell.y() );
238 }
239 
241 {
242  Grid::iterator it = m_grid.find( QPoint( col, row ) );
243  if( it == m_grid.end() )
244  {
245  return NULL;
246  }
247  return ( *it ).second;
248 }
249 
250 QGraphicsItem* WQtNetworkItemGrid::setItemImpl( QGraphicsItem* item, int col, int row, bool suppressUpdate )
251 {
252  // keep the item that might be there
253  QGraphicsItem* prev = m_grid[ QPoint( col, row ) ];
254 
255  // set the new item to the grid
256  m_grid[ QPoint( col, row ) ] = item;
257  // and store the coordinates for the item
258  m_gridReverse[ item ] = QPoint( col, row );
259 
260  // grid might be larger now
261  if( !suppressUpdate )
262  {
264  }
265 
266  return prev;
267 }
268 
269 bool WQtNetworkItemGrid::removeItemImpl( QGraphicsItem* item, bool suppressUpdate )
270 {
271  // item exists?
272  ItemCoordinateMap::iterator it = m_gridReverse.find( item );
273  if( it == m_gridReverse.end() )
274  {
275  return false;
276  }
277 
278  // ok found. Where?
279  QPoint where = ( *it ).second;
280  // remove from old position
281  m_grid.erase( where );
282 
283  // remove from reverse list
284  m_gridReverse.erase( it );
285 
286  // grid might be smaller now
287  if( !suppressUpdate )
288  {
290  }
291 
292  return true;
293 }
294 
296 {
297  // we assume that the remove and move functions clean up empty lists
298  return m_dimensions.x() + m_dimensions.width();
299 }
300 
301 QPointF WQtNetworkItemGrid::mapToWorld( int col, int row )
302 {
303  return QPointF( col * ( WNETWORKITEM_XSPACE + WNETWORKITEM_MAXIMUM_WIDTH ),
304  row * ( WNETWORKITEM_YSPACE + WNETWORKITEM_MAXIMUM_HEIGHT ) );
305 }
306 
307 QPointF WQtNetworkItemGrid::mapToWorld( QPoint gridCoord )
308 {
309  return mapToWorld( gridCoord.x(), gridCoord.y() );
310 }
311 
312 QRectF WQtNetworkItemGrid::mapCellAreaToWorld( int col, int row )
313 {
314  QPointF c = mapToWorld( col, row );
315  return QRectF( c.x(), c.y(), WNETWORKITEM_XSPACE + WNETWORKITEM_MAXIMUM_WIDTH, WNETWORKITEM_YSPACE + WNETWORKITEM_MAXIMUM_HEIGHT );
316 }
317 
319 {
320  return mapCellAreaToWorld( cell.x(), cell.y() );
321 }
322 
324 {
325  ItemCoordinateMap::iterator it = m_gridReverse.find( item );
326  if( it == m_gridReverse.end() )
327  {
328  return QPoint();
329  }
330  return ( *it ).second;
331 }
332 
334 {
335  ItemCoordinateMap::iterator it = m_gridReverse.find( item );
336  return ( it != m_gridReverse.end() );
337 }
338 
340 {
341  // NOTE: this is very important to allow QGraphicsScene updating its index before geometry change
342  prepareGeometryChange();
343 
344  QRect oldDim = m_dimensions;
345  QPoint min;
346  QPoint max;
347 
348  bool first = true;
349  for( Grid::const_iterator it = m_grid.begin(); it != m_grid.end(); ++it )
350  {
351  // empty grid cell?
352  if( !( *it ).second )
353  {
354  continue;
355  }
356 
357  QPoint p = ( *it ).first;
358 
359  // extend bounds by this point
360  if( first )
361  {
362  first = false;
363  min = p;
364  max = p;
365  }
366  else
367  {
368  min.rx() = std::min( p.x(), min.x() );
369  min.ry() = std::min( p.y(), min.y() );
370  max.rx() = std::max( p.x(), max.x() );
371  max.ry() = std::max( p.y(), max.y() );
372  }
373  }
374 
375  if( !first ) // there really was something in the grid
376  {
377  m_dimensions = QRect( min, max );
378  }
379  else
380  {
381  m_dimensions = QRect();
382  }
383 
384  // changed?
385  if( oldDim != m_dimensions )
386  {
387  // Debug
388  // wlog::debug( "new BB") << m_dimensions.topLeft().x() << ", " << m_dimensions.topLeft().y()
389  // << m_dimensions.bottomRight().x() << ", " << m_dimensions.bottomRight().y() << " ---- " << m_dimensions.width();
391  }
392 }
393 
394 QPoint WQtNetworkItemGrid::findNearestCell( QPointF worldSpace )
395 {
396  float boxWidth = WNETWORKITEM_XSPACE + WNETWORKITEM_MAXIMUM_WIDTH;
397  float boxHeight = WNETWORKITEM_YSPACE + WNETWORKITEM_MAXIMUM_HEIGHT;
398 
399  // NOTE: (0,0) in world space overlaps with (0,0) in grid space
400  return QPoint( std::floor( worldSpace.x() / boxWidth ),
401  std::floor( worldSpace.y() / boxHeight ) );
402 }
403 
404 void WQtNetworkItemGrid::highlightCell( QPoint cell, QColor color )
405 {
406  // NOTE: this is very important to allow QGraphicsScene updating its index before geometry change
407  prepareGeometryChange();
408 
409  // also update bounding area of item or QGraphicsScene might not draw the highlight cell
410  m_highlightCell = cell;
411  m_highlightColor = color;
412  m_highlightCellEnabled = true;
414  update();
415 }
416 
418 {
419  // NOTE: this is very important to allow QGraphicsScene updating its index before geometry change
420  prepareGeometryChange();
421  m_highlightCellEnabled = false;
423  update();
424 }
425 
427 {
428  // if currently blending out, stop it and continue with blending in at this position
429  if( m_blendOutTimer->state() == QTimeLine::Running )
430  {
431  m_blendOutTimer->stop();
432  float doneRatio = static_cast< float >( m_blendOutTimer->currentTime() ) / static_cast< float >( m_blendInTimer->duration() );
433  float remaining = doneRatio * static_cast< float >( m_blendInTimer->duration() );
434  m_blendInTimer->setCurrentTime( static_cast< int >( remaining ) );
435  }
436  else
437  {
438  m_blendInTimer->setCurrentTime( 0 );
439  }
440 
441  m_blendInTimer->start();
442 }
443 
445 {
446  // if currently blending out, stop it and continue with blending in at this position
447  if( m_blendInTimer->state() == QTimeLine::Running )
448  {
449  m_blendInTimer->stop();
450  float doneRatio = static_cast< float >( m_blendInTimer->currentTime() ) / static_cast< float >( m_blendInTimer->duration() );
451  float remaining = doneRatio * static_cast< float >( m_blendOutTimer->duration() );
452  m_blendOutTimer->setCurrentTime( static_cast< int >( remaining ) );
453  }
454  else
455  {
456  m_blendOutTimer->setCurrentTime( 0 );
457  }
458 
459  m_blendOutTimer->resume();
460 }
461 
463 {
464  setOpacity( value * value );
465 }
466 
468 {
469  setOpacity( 1.0 - ( value * value ) );
470 }
471 
473 {
474  m_disableUpdateBounds = disable;
475 }
476 
478 {
479  return m_dimensions;
480 }
481 
QRectF m_bb
Bounding Rect of the Grid in world coordinates.
ItemCoordinateMap m_gridReverse
Keeps track of the item coordinates.
virtual QGraphicsItem * at(int col, int row)
Return the element at the given position.
bool setItem(QGraphicsItem *item, int col, int row)
Set the specified item to the specified position.
virtual QRectF boundingRect() const
The bounding area of the item.
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
Paint the item.
virtual QPoint whereIs(QGraphicsItem *item)
Find the position of the specified item.
QTimeLine * m_blendInTimer
Timer used for blend in effects of the grid.
bool m_disableUpdateBounds
Disable updateBounds signal.
bool moveItem(QGraphicsItem *item, int col, int row)
Move item from its current position to the specified one.
virtual QPoint findNearestCell(QPointF worldSpace)
Find the nearest cell matching the specified world coordinates.
virtual void physicalMoveTo(QGraphicsItem *item, int col, int row, bool animate=true)
Move the item to the position physically.
virtual ~WQtNetworkItemGrid()
Destructor.
void updateBoundingRect()
Use to update bounding box when you modify the m_grid dimensions.
virtual bool isInGrid(QGraphicsItem *item)
Check whether the item is managed in the grid.
virtual QRectF mapCellAreaToWorld(QPoint cell)
Build a rect which covers the cell area in world space.
void animationBlendInTick(qreal value)
Called when the animation timers tick and progress in timeline.
WQtNetworkItemGrid()
Default constructor.
virtual QGraphicsItem * setItemImpl(QGraphicsItem *item, int col, int row, bool suppressUpdate=false)
Really sets the item to the grid position.
Grid m_grid
The virtual grid.
virtual QRect getGridBoundingRect() const
Returns the boundaries in grid coordinates.
QTimeLine * m_blendOutTimer
Timer used for blend in effects of the grid.
QRect m_dimensions
The largest number of entries in a column/row.
void blendOut()
Allows blending out the underlaying layout structure.
void updateDimensions()
Update the m_dimensions.
void highlightCell()
Turns off highlight.
virtual QPointF mapToWorld(int col, int row)
Map the grid coordinate to world coordinates.
void disableBoundsUpdate(bool disable=true)
Allows for temporarily disabling bounds update signal.
void blendIn()
Allows blending in the underlaying layout structure.
QColor m_highlightColor
Color of the highlight.
void removeItem(int col, int row)
Remove the item at the given position.
int getFirstFreeColumn() const
Return the index of the first empty column.
bool m_highlightCellEnabled
En/Dis-able highlughting.
bool isOccupied(int col, int row)
Is there an element at the given position?
void animationBlendOutTick(qreal value)
Called when the animation timers tick and progress in timeline.
void updatedBounds()
emitted when the grid shrinks or grows to update scene bounds
virtual bool removeItemImpl(QGraphicsItem *item, bool suppressUpdate=false)
Remove the item from the grid if existing.
QPoint m_highlightCell
Current cell to highlight.
This class represents a WModule as QGraphicsItem and contains a reference to its in- and outports.
void animatedMoveTo(QPointF pos)
Move item to specified position smoothly, via animation.
void moveTo(QPointF pos)
Move item to specified position smoothly, no animation.