1 | #include "graph.h"
|
---|
2 | #include <qcanvas.h>
|
---|
3 | #include <stdlib.h>
|
---|
4 | #include <qdatetime.h>
|
---|
5 | #include <qhbox.h>
|
---|
6 | #include <qpushbutton.h>
|
---|
7 | #include <qslider.h>
|
---|
8 | #include <qlabel.h>
|
---|
9 | #include <qlayout.h>
|
---|
10 |
|
---|
11 | const int bounce_rtti = 1234;
|
---|
12 |
|
---|
13 | // We use a global variable to save memory - all the brushes and pens in
|
---|
14 | // the mesh are shared.
|
---|
15 | static QBrush *tb = 0;
|
---|
16 | static QPen *tp = 0;
|
---|
17 |
|
---|
18 | class EdgeItem;
|
---|
19 | class NodeItem;
|
---|
20 | class FigureEditor;
|
---|
21 | typedef QValueList<NodeItem*> NodeItemList;
|
---|
22 | typedef QValueList<EdgeItem*> EdgeItemList;
|
---|
23 |
|
---|
24 | #define SPEED2ADVANCE(x) (301-x)
|
---|
25 |
|
---|
26 | class GraphWidgetPrivate
|
---|
27 | {
|
---|
28 | public:
|
---|
29 | GraphWidgetPrivate() {
|
---|
30 | moving = 0;
|
---|
31 | speed = 275;
|
---|
32 | }
|
---|
33 | ~GraphWidgetPrivate() {
|
---|
34 | delete canvas;
|
---|
35 | }
|
---|
36 | NodeItemList nodeItems;
|
---|
37 | FigureEditor* editor;
|
---|
38 | QCanvas* canvas;
|
---|
39 | QCanvasItem* moving;
|
---|
40 | int speed;
|
---|
41 | };
|
---|
42 |
|
---|
43 | class EdgeItem: public QCanvasLine
|
---|
44 | {
|
---|
45 | public:
|
---|
46 | EdgeItem( NodeItem*, NodeItem*, QCanvas* );
|
---|
47 | void setFromPoint( int x, int y ) ;
|
---|
48 | void setToPoint( int x, int y );
|
---|
49 | void moveBy(double dx, double dy);
|
---|
50 |
|
---|
51 | NodeItem* from;
|
---|
52 | NodeItem* to;
|
---|
53 | };
|
---|
54 |
|
---|
55 |
|
---|
56 |
|
---|
57 | class NodeItem: public QCanvasEllipse
|
---|
58 | {
|
---|
59 | public:
|
---|
60 | NodeItem( GraphWidgetPrivate* g );
|
---|
61 | ~NodeItem() {}
|
---|
62 |
|
---|
63 | void addInEdge( EdgeItem *edge ) { inList.append( edge ); }
|
---|
64 | void addOutEdge( EdgeItem *edge ) { outList.append( edge ); }
|
---|
65 |
|
---|
66 | void moveBy(double dx, double dy);
|
---|
67 |
|
---|
68 | void calcForce();
|
---|
69 | void advance( int stage );
|
---|
70 |
|
---|
71 | private:
|
---|
72 | GraphWidgetPrivate* graph;
|
---|
73 | EdgeItemList inList;
|
---|
74 | EdgeItemList outList;
|
---|
75 | };
|
---|
76 |
|
---|
77 |
|
---|
78 |
|
---|
79 | void EdgeItem::moveBy(double, double)
|
---|
80 | {
|
---|
81 | //nothing
|
---|
82 | }
|
---|
83 |
|
---|
84 | EdgeItem::EdgeItem( NodeItem *fromItem, NodeItem *toItem, QCanvas *canvas )
|
---|
85 | : QCanvasLine( canvas )
|
---|
86 | {
|
---|
87 | from = fromItem;
|
---|
88 | to = toItem;
|
---|
89 | setPen( *tp );
|
---|
90 | setBrush( *tb );
|
---|
91 | from->addOutEdge( this );
|
---|
92 | to->addInEdge( this );
|
---|
93 | setPoints( int(from->x()), int(from->y()), int(to->x()), int(to->y()) );
|
---|
94 | setZ( 127 );
|
---|
95 | }
|
---|
96 |
|
---|
97 | void EdgeItem::setFromPoint( int x, int y )
|
---|
98 | {
|
---|
99 | setPoints( x,y, endPoint().x(), endPoint().y() );
|
---|
100 | }
|
---|
101 |
|
---|
102 | void EdgeItem::setToPoint( int x, int y )
|
---|
103 | {
|
---|
104 | setPoints( startPoint().x(), startPoint().y(), x, y );
|
---|
105 | }
|
---|
106 |
|
---|
107 |
|
---|
108 | void NodeItem::moveBy(double dx, double dy)
|
---|
109 | {
|
---|
110 | double nx = x() + dx;
|
---|
111 | double ny = y() + dy;
|
---|
112 | if ( graph->moving != this ) {
|
---|
113 | nx = QMAX( width()/2, nx );
|
---|
114 | ny = QMAX( height()/2, ny );
|
---|
115 | nx = QMIN( canvas()->width() - width()/2, nx );
|
---|
116 | ny = QMIN( canvas()->height() - height()/2, ny );
|
---|
117 | }
|
---|
118 | QCanvasEllipse::moveBy( nx-x(), ny-y() );
|
---|
119 | EdgeItemList::Iterator it;
|
---|
120 | for ( it = inList.begin(); it != inList.end(); ++it )
|
---|
121 | (*it)->setToPoint( int(x()), int(y()) );
|
---|
122 | for ( it = outList.begin(); it != outList.end(); ++it )
|
---|
123 | (*it)->setFromPoint( int(x()), int(y()) );
|
---|
124 | }
|
---|
125 |
|
---|
126 | NodeItem::NodeItem( GraphWidgetPrivate* g )
|
---|
127 | : QCanvasEllipse( 32, 32, g->canvas )
|
---|
128 | {
|
---|
129 | graph = g;
|
---|
130 | graph->nodeItems.append( this );
|
---|
131 | setPen( *tp );
|
---|
132 | setBrush( *tb );
|
---|
133 | setZ( 128 );
|
---|
134 | }
|
---|
135 |
|
---|
136 | void NodeItem::advance( int stage ) {
|
---|
137 | switch ( stage ) {
|
---|
138 | case 0:
|
---|
139 | calcForce();
|
---|
140 | break;
|
---|
141 | case 1:
|
---|
142 | QCanvasItem::advance(stage);
|
---|
143 | break;
|
---|
144 | }
|
---|
145 | }
|
---|
146 |
|
---|
147 | void NodeItem::calcForce() {
|
---|
148 | if ( graph->moving == this ) {
|
---|
149 | setVelocity( 0, 0 );
|
---|
150 | return;
|
---|
151 | }
|
---|
152 | double xvel = 0;
|
---|
153 | double yvel = 0;
|
---|
154 | for ( NodeItemList::Iterator it = graph->nodeItems.begin(); it != graph->nodeItems.end(); ++it ) {
|
---|
155 | NodeItem* n = (*it);
|
---|
156 | if ( n == this )
|
---|
157 | continue;
|
---|
158 | double dx = x() - n->x();
|
---|
159 | double dy = y() - n->y();
|
---|
160 | double l = 2 * ( dx * dx + dy * dy );
|
---|
161 | if ( l > 0 ) {
|
---|
162 | xvel = xvel + dx*260 / l;
|
---|
163 | yvel = yvel + dy*260 / l;
|
---|
164 | }
|
---|
165 | }
|
---|
166 | double w = 1 + outList.count() + inList.count();
|
---|
167 | w *= 10;
|
---|
168 | EdgeItemList::Iterator it2;
|
---|
169 | EdgeItem * e;
|
---|
170 | NodeItem* n;
|
---|
171 | for ( it2 = outList.begin(); it2 != outList.end(); ++it2 ) {
|
---|
172 | e = (*it2);
|
---|
173 | n = e->to;
|
---|
174 | xvel = xvel - ( x() - n->x() ) / w;
|
---|
175 | yvel = yvel - ( y() - n->y() ) / w;
|
---|
176 | }
|
---|
177 | for ( it2 = inList.begin(); it2 != inList.end(); ++it2 ) {
|
---|
178 | e = (*it2);
|
---|
179 | n = e->from;
|
---|
180 | xvel = xvel - ( x() - n->x() ) / w;
|
---|
181 | yvel = yvel - ( y() - n->y() ) / w;
|
---|
182 | }
|
---|
183 | if ( QABS( xvel ) < .1 && QABS( yvel ) < .1 )
|
---|
184 | xvel = yvel = 0;
|
---|
185 | setVelocity( xvel, yvel );
|
---|
186 | }
|
---|
187 |
|
---|
188 |
|
---|
189 | class FigureEditor : public QCanvasView {
|
---|
190 | public:
|
---|
191 | FigureEditor( GraphWidgetPrivate *g, QWidget* parent=0, const char* name=0, WFlags f=0);
|
---|
192 |
|
---|
193 | QSize sizeHint() const;
|
---|
194 |
|
---|
195 |
|
---|
196 | protected:
|
---|
197 | void contentsMousePressEvent(QMouseEvent*);
|
---|
198 | void contentsMouseReleaseEvent(QMouseEvent*);
|
---|
199 | void contentsMouseMoveEvent(QMouseEvent*);
|
---|
200 |
|
---|
201 |
|
---|
202 | void resizeEvent( QResizeEvent* );
|
---|
203 | void showEvent( QShowEvent* );
|
---|
204 | void hideEvent( QHideEvent* e);
|
---|
205 |
|
---|
206 | private:
|
---|
207 | void initialize();
|
---|
208 | QPoint moving_start;
|
---|
209 | GraphWidgetPrivate* graph;
|
---|
210 | };
|
---|
211 |
|
---|
212 |
|
---|
213 | FigureEditor::FigureEditor(
|
---|
214 | GraphWidgetPrivate* g, QWidget* parent,
|
---|
215 | const char* name, WFlags f) :
|
---|
216 | QCanvasView(g->canvas, parent,name,f)
|
---|
217 | {
|
---|
218 | graph = g;
|
---|
219 | }
|
---|
220 |
|
---|
221 | void FigureEditor::contentsMousePressEvent(QMouseEvent* e)
|
---|
222 | {
|
---|
223 | QCanvasItemList l=canvas()->collisions(e->pos());
|
---|
224 | for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
|
---|
225 | if ((*it)->rtti()==bounce_rtti )
|
---|
226 | continue;
|
---|
227 | graph->moving = *it;
|
---|
228 | moving_start = e->pos();
|
---|
229 | return;
|
---|
230 | }
|
---|
231 | graph->moving = 0;
|
---|
232 | }
|
---|
233 |
|
---|
234 | void FigureEditor::contentsMouseReleaseEvent(QMouseEvent* )
|
---|
235 | {
|
---|
236 | if ( graph->moving )
|
---|
237 | graph->moving = 0;
|
---|
238 | }
|
---|
239 |
|
---|
240 | void FigureEditor::contentsMouseMoveEvent(QMouseEvent* e)
|
---|
241 | {
|
---|
242 | if ( graph->moving ) {
|
---|
243 | graph->moving->moveBy(e->pos().x() - moving_start.x(),
|
---|
244 | e->pos().y() - moving_start.y());
|
---|
245 | moving_start = e->pos();
|
---|
246 | canvas()->update();
|
---|
247 | }
|
---|
248 | }
|
---|
249 |
|
---|
250 | class BouncyText : public QCanvasText {
|
---|
251 | void initPos();
|
---|
252 | void initSpeed();
|
---|
253 | public:
|
---|
254 | int rtti() const;
|
---|
255 | BouncyText(const QString&, QFont, QCanvas*);
|
---|
256 | void advance(int);
|
---|
257 | };
|
---|
258 |
|
---|
259 | BouncyText::BouncyText( const QString& text, QFont f, QCanvas* canvas) :
|
---|
260 | QCanvasText(text, f, canvas)
|
---|
261 | {
|
---|
262 | setAnimated(TRUE);
|
---|
263 | initPos();
|
---|
264 | }
|
---|
265 |
|
---|
266 |
|
---|
267 | int BouncyText::rtti() const
|
---|
268 | {
|
---|
269 | return bounce_rtti;
|
---|
270 | }
|
---|
271 |
|
---|
272 | void BouncyText::initPos()
|
---|
273 | {
|
---|
274 | initSpeed();
|
---|
275 | int trial=1000;
|
---|
276 | do {
|
---|
277 | move(rand()%(canvas()->width()-boundingRect().width()),
|
---|
278 | rand()%(canvas()->height()-boundingRect().height()));
|
---|
279 | advance(0);
|
---|
280 | } while (trial-- && xVelocity()==0.0 && yVelocity()==0.0);
|
---|
281 | }
|
---|
282 |
|
---|
283 | void BouncyText::initSpeed()
|
---|
284 | {
|
---|
285 | const double speed = 2.0;
|
---|
286 | double d = (double)(rand()%1024) / 1024.0;
|
---|
287 | double e = (double)(rand()%1024) / 1024.0;
|
---|
288 |
|
---|
289 | if ( d < .5 )
|
---|
290 | d = -1 - d;
|
---|
291 | else
|
---|
292 | d = d + 1;
|
---|
293 | if ( e < .5 )
|
---|
294 | e = -1 - e;
|
---|
295 | else
|
---|
296 | e = e + 1;
|
---|
297 |
|
---|
298 | setVelocity( d*speed, e * speed );
|
---|
299 | }
|
---|
300 |
|
---|
301 | void BouncyText::advance( int stage )
|
---|
302 | {
|
---|
303 | switch ( stage ) {
|
---|
304 | case 0: {
|
---|
305 | double vx = xVelocity();
|
---|
306 | double vy = yVelocity();
|
---|
307 |
|
---|
308 | if ( vx == 0.0 && vy == 0.0 ) {
|
---|
309 | // stopped last turn
|
---|
310 | initSpeed();
|
---|
311 | vx = xVelocity();
|
---|
312 | vy = yVelocity();
|
---|
313 | }
|
---|
314 |
|
---|
315 | QRect r = boundingRect();
|
---|
316 | r.moveBy( int(vx), int(vy) );
|
---|
317 |
|
---|
318 | if ( r.left() < 0 || r.right() > canvas()->width() )
|
---|
319 | vx = -vx;
|
---|
320 | if ( r.top() < 0 || r.bottom() > canvas()->height() )
|
---|
321 | vy = -vy;
|
---|
322 |
|
---|
323 | r = boundingRect();
|
---|
324 | r.moveBy( int(vx), int(vy) );
|
---|
325 | if ( r.left() < 0 || r.right() > canvas()->width() )
|
---|
326 | vx = 0;
|
---|
327 | if ( r.top() < 0 || r.bottom() > canvas()->height() )
|
---|
328 | vy = 0;
|
---|
329 |
|
---|
330 | setVelocity( vx, vy );
|
---|
331 | } break;
|
---|
332 | case 1:
|
---|
333 | QCanvasItem::advance( stage );
|
---|
334 | break;
|
---|
335 | }
|
---|
336 | }
|
---|
337 |
|
---|
338 | GraphWidget::GraphWidget( QWidget *parent, const char *name)
|
---|
339 | : QWidget( parent, name )
|
---|
340 | {
|
---|
341 | d = new GraphWidgetPrivate;
|
---|
342 | d->canvas = 0;
|
---|
343 | QVBoxLayout* vb = new QVBoxLayout( this, 11, 6 );
|
---|
344 | d->editor = new FigureEditor( d, this );
|
---|
345 | vb->addWidget( d->editor );
|
---|
346 | QHBoxLayout* hb = new QHBoxLayout( vb );
|
---|
347 | hb->addWidget( new QLabel("Slow", this ) );
|
---|
348 | QSlider* slider = new QSlider( 0, 300, 25, d->speed, Horizontal, this );
|
---|
349 | connect( slider, SIGNAL( valueChanged(int) ), this, SLOT( setSpeed(int) ) );
|
---|
350 | hb->addWidget( slider );
|
---|
351 | hb->addWidget( new QLabel("Fast", this ) );
|
---|
352 | hb->addSpacing( 10 );
|
---|
353 | QPushButton* btn = new QPushButton( "Shuffle Nodes", this );
|
---|
354 | connect( btn, SIGNAL( clicked() ), this, SLOT( shuffle() ) );
|
---|
355 | hb->addWidget( btn );
|
---|
356 | }
|
---|
357 |
|
---|
358 |
|
---|
359 | GraphWidget::~GraphWidget()
|
---|
360 | {
|
---|
361 | delete d;
|
---|
362 | }
|
---|
363 |
|
---|
364 | void GraphWidget::setSpeed(int s)
|
---|
365 | {
|
---|
366 | d->speed = s;
|
---|
367 | if ( isVisible() && d->canvas )
|
---|
368 | d->canvas->setAdvancePeriod( SPEED2ADVANCE( s ) );
|
---|
369 | }
|
---|
370 |
|
---|
371 | void GraphWidget::shuffle()
|
---|
372 | {
|
---|
373 |
|
---|
374 | for ( NodeItemList::Iterator it = d->nodeItems.begin(); it != d->nodeItems.end(); ++it ) {
|
---|
375 | NodeItem* ni = (*it);
|
---|
376 | ni->move(rand()%(d->canvas->width()-ni->width()),rand()%(d->canvas->height()-ni->height()));
|
---|
377 | }
|
---|
378 | }
|
---|
379 |
|
---|
380 |
|
---|
381 | QSize FigureEditor::sizeHint() const
|
---|
382 | {
|
---|
383 | return QSize( 600, 400 );
|
---|
384 | }
|
---|
385 |
|
---|
386 | void FigureEditor::resizeEvent( QResizeEvent* e )
|
---|
387 | {
|
---|
388 | if ( canvas() )
|
---|
389 | canvas()->resize( contentsRect().width(), contentsRect().height() );
|
---|
390 | QCanvasView::resizeEvent( e );
|
---|
391 | }
|
---|
392 |
|
---|
393 | void FigureEditor::showEvent( QShowEvent* )
|
---|
394 | {
|
---|
395 | initialize();
|
---|
396 | canvas()->setAdvancePeriod( SPEED2ADVANCE(graph->speed) );
|
---|
397 | }
|
---|
398 |
|
---|
399 | void FigureEditor::hideEvent( QHideEvent* )
|
---|
400 | {
|
---|
401 | initialize();
|
---|
402 | canvas()->setAdvancePeriod( -10 );
|
---|
403 | }
|
---|
404 |
|
---|
405 | void FigureEditor::initialize()
|
---|
406 | {
|
---|
407 | if ( canvas() )
|
---|
408 | return;
|
---|
409 | resize( sizeHint() );
|
---|
410 | graph->canvas = new QCanvas( contentsRect().width(), contentsRect().height() );
|
---|
411 | if ( !tb ) tb = new QBrush( Qt::red );
|
---|
412 | if ( !tp ) tp = new QPen( Qt::black );
|
---|
413 | srand( QTime::currentTime().msec() );
|
---|
414 | int nodecount = 0;
|
---|
415 |
|
---|
416 | int rows = 3;
|
---|
417 | int cols = 3;
|
---|
418 |
|
---|
419 | QMemArray<NodeItem*> lastRow(cols);
|
---|
420 | for ( int r = 0; r < rows; r++ ) {
|
---|
421 | NodeItem *prev = 0;
|
---|
422 | for ( int c = 0; c < cols; c++ ) {
|
---|
423 | NodeItem *ni = new NodeItem( graph );
|
---|
424 | ni->setAnimated( TRUE );
|
---|
425 | nodecount++;
|
---|
426 | ni->move(rand()%(graph->canvas->width()-ni->width()),rand()%(graph->canvas->height()-ni->height()));
|
---|
427 |
|
---|
428 | if ( r > 0 )
|
---|
429 | (new EdgeItem( lastRow[c], ni, graph->canvas ))->show();
|
---|
430 | if ( prev )
|
---|
431 | (new EdgeItem( prev, ni, graph->canvas ))->show();
|
---|
432 | prev = ni;
|
---|
433 | lastRow[c] = ni;
|
---|
434 | ni->show();
|
---|
435 | }
|
---|
436 | }
|
---|
437 |
|
---|
438 | graph->canvas->advance();
|
---|
439 |
|
---|
440 | QCanvasItem* i = new BouncyText( tr( "Drag the nodes around!" ), QFont("helvetica", 24), graph->canvas);
|
---|
441 | i->show();
|
---|
442 | setCanvas( graph->canvas );
|
---|
443 | setMinimumSize( 600, 400 );
|
---|
444 | setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
|
---|
445 | }
|
---|