You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
qt3/examples/demo/graph.cpp

446 lines
9.9 KiB

#include "graph.h"
#include <qcanvas.h>
#include <stdlib.h>
#include <qdatetime.h>
#include <qhbox.h>
#include <qpushbutton.h>
#include <qslider.h>
#include <qlabel.h>
#include <qlayout.h>
const int bounce_rtti = 1234;
// We use a global variable to save memory - all the brushes and pens in
// the mesh are shared.
static QBrush *tb = 0;
static QPen *tp = 0;
class EdgeItem;
class NodeItem;
class FigureEditor;
typedef QValueList<NodeItem*> NodeItemList;
typedef QValueList<EdgeItem*> EdgeItemList;
#define SPEED2ADVANCE(x) (301-x)
class GraphWidgetPrivate
{
public:
GraphWidgetPrivate() {
moving = 0;
speed = 275;
}
~GraphWidgetPrivate() {
delete canvas;
}
NodeItemList nodeItems;
FigureEditor* editor;
QCanvas* canvas;
QCanvasItem* moving;
int speed;
};
class EdgeItem: public QCanvasLine
{
public:
EdgeItem( NodeItem*, NodeItem*, QCanvas* );
void setFromPoint( int x, int y ) ;
void setToPoint( int x, int y );
void moveBy(double dx, double dy);
NodeItem* from;
NodeItem* to;
};
class NodeItem: public QCanvasEllipse
{
public:
NodeItem( GraphWidgetPrivate* g );
~NodeItem() {}
void addInEdge( EdgeItem *edge ) { inList.append( edge ); }
void addOutEdge( EdgeItem *edge ) { outList.append( edge ); }
void moveBy(double dx, double dy);
void calcForce();
void advance( int stage );
private:
GraphWidgetPrivate* graph;
EdgeItemList inList;
EdgeItemList outList;
};
void EdgeItem::moveBy(double, double)
{
//nothing
}
EdgeItem::EdgeItem( NodeItem *fromItem, NodeItem *toItem, QCanvas *canvas )
: QCanvasLine( canvas )
{
from = fromItem;
to = toItem;
setPen( *tp );
setBrush( *tb );
from->addOutEdge( this );
to->addInEdge( this );
setPoints( int(from->x()), int(from->y()), int(to->x()), int(to->y()) );
setZ( 127 );
}
void EdgeItem::setFromPoint( int x, int y )
{
setPoints( x,y, endPoint().x(), endPoint().y() );
}
void EdgeItem::setToPoint( int x, int y )
{
setPoints( startPoint().x(), startPoint().y(), x, y );
}
void NodeItem::moveBy(double dx, double dy)
{
double nx = x() + dx;
double ny = y() + dy;
if ( graph->moving != this ) {
nx = QMAX( width()/2, nx );
ny = QMAX( height()/2, ny );
nx = QMIN( canvas()->width() - width()/2, nx );
ny = QMIN( canvas()->height() - height()/2, ny );
}
QCanvasEllipse::moveBy( nx-x(), ny-y() );
EdgeItemList::Iterator it;
for ( it = inList.begin(); it != inList.end(); ++it )
(*it)->setToPoint( int(x()), int(y()) );
for ( it = outList.begin(); it != outList.end(); ++it )
(*it)->setFromPoint( int(x()), int(y()) );
}
NodeItem::NodeItem( GraphWidgetPrivate* g )
: QCanvasEllipse( 32, 32, g->canvas )
{
graph = g;
graph->nodeItems.append( this );
setPen( *tp );
setBrush( *tb );
setZ( 128 );
}
void NodeItem::advance( int stage ) {
switch ( stage ) {
case 0:
calcForce();
break;
case 1:
QCanvasItem::advance(stage);
break;
}
}
void NodeItem::calcForce() {
if ( graph->moving == this ) {
setVelocity( 0, 0 );
return;
}
double xvel = 0;
double yvel = 0;
for ( NodeItemList::Iterator it = graph->nodeItems.begin(); it != graph->nodeItems.end(); ++it ) {
NodeItem* n = (*it);
if ( n == this )
continue;
double dx = x() - n->x();
double dy = y() - n->y();
double l = 2 * ( dx * dx + dy * dy );
if ( l > 0 ) {
xvel = xvel + dx*260 / l;
yvel = yvel + dy*260 / l;
}
}
double w = 1 + outList.count() + inList.count();
w *= 10;
EdgeItemList::Iterator it2;
EdgeItem * e;
NodeItem* n;
for ( it2 = outList.begin(); it2 != outList.end(); ++it2 ) {
e = (*it2);
n = e->to;
xvel = xvel - ( x() - n->x() ) / w;
yvel = yvel - ( y() - n->y() ) / w;
}
for ( it2 = inList.begin(); it2 != inList.end(); ++it2 ) {
e = (*it2);
n = e->from;
xvel = xvel - ( x() - n->x() ) / w;
yvel = yvel - ( y() - n->y() ) / w;
}
if ( QABS( xvel ) < .1 && QABS( yvel ) < .1 )
xvel = yvel = 0;
setVelocity( xvel, yvel );
}
class FigureEditor : public QCanvasView {
public:
FigureEditor( GraphWidgetPrivate *g, QWidget* parent=0, const char* name=0, WFlags f=0);
QSize sizeHint() const;
protected:
void contentsMousePressEvent(QMouseEvent*);
void contentsMouseReleaseEvent(QMouseEvent*);
void contentsMouseMoveEvent(QMouseEvent*);
void resizeEvent( QResizeEvent* );
void showEvent( QShowEvent* );
void hideEvent( QHideEvent* e);
private:
void initialize();
QPoint moving_start;
GraphWidgetPrivate* graph;
};
FigureEditor::FigureEditor(
GraphWidgetPrivate* g, QWidget* parent,
const char* name, WFlags f) :
QCanvasView(g->canvas, parent,name,f)
{
graph = g;
}
void FigureEditor::contentsMousePressEvent(QMouseEvent* e)
{
QCanvasItemList l=canvas()->collisions(e->pos());
for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
if ((*it)->rtti()==bounce_rtti )
continue;
graph->moving = *it;
moving_start = e->pos();
return;
}
graph->moving = 0;
}
void FigureEditor::contentsMouseReleaseEvent(QMouseEvent* )
{
if ( graph->moving )
graph->moving = 0;
}
void FigureEditor::contentsMouseMoveEvent(QMouseEvent* e)
{
if ( graph->moving ) {
graph->moving->moveBy(e->pos().x() - moving_start.x(),
e->pos().y() - moving_start.y());
moving_start = e->pos();
canvas()->update();
}
}
class BouncyText : public QCanvasText {
void initPos();
void initSpeed();
public:
int rtti() const;
BouncyText(const QString&, QFont, QCanvas*);
void advance(int);
};
BouncyText::BouncyText( const QString& text, QFont f, QCanvas* canvas) :
QCanvasText(text, f, canvas)
{
setAnimated(TRUE);
initPos();
}
int BouncyText::rtti() const
{
return bounce_rtti;
}
void BouncyText::initPos()
{
initSpeed();
int trial=1000;
do {
move(rand()%(canvas()->width()-boundingRect().width()),
rand()%(canvas()->height()-boundingRect().height()));
advance(0);
} while (trial-- && xVelocity()==0.0 && yVelocity()==0.0);
}
void BouncyText::initSpeed()
{
const double speed = 2.0;
double d = (double)(rand()%1024) / 1024.0;
double e = (double)(rand()%1024) / 1024.0;
if ( d < .5 )
d = -1 - d;
else
d = d + 1;
if ( e < .5 )
e = -1 - e;
else
e = e + 1;
setVelocity( d*speed, e * speed );
}
void BouncyText::advance( int stage )
{
switch ( stage ) {
case 0: {
double vx = xVelocity();
double vy = yVelocity();
if ( vx == 0.0 && vy == 0.0 ) {
// stopped last turn
initSpeed();
vx = xVelocity();
vy = yVelocity();
}
QRect r = boundingRect();
r.moveBy( int(vx), int(vy) );
if ( r.left() < 0 || r.right() > canvas()->width() )
vx = -vx;
if ( r.top() < 0 || r.bottom() > canvas()->height() )
vy = -vy;
r = boundingRect();
r.moveBy( int(vx), int(vy) );
if ( r.left() < 0 || r.right() > canvas()->width() )
vx = 0;
if ( r.top() < 0 || r.bottom() > canvas()->height() )
vy = 0;
setVelocity( vx, vy );
} break;
case 1:
QCanvasItem::advance( stage );
break;
}
}
GraphWidget::GraphWidget( QWidget *parent, const char *name)
: QWidget( parent, name )
{
d = new GraphWidgetPrivate;
d->canvas = 0;
QVBoxLayout* vb = new QVBoxLayout( this, 11, 6 );
d->editor = new FigureEditor( d, this );
vb->addWidget( d->editor );
QHBoxLayout* hb = new QHBoxLayout( vb );
hb->addWidget( new QLabel("Slow", this ) );
QSlider* slider = new QSlider( 0, 300, 25, d->speed, Horizontal, this );
connect( slider, SIGNAL( valueChanged(int) ), this, SLOT( setSpeed(int) ) );
hb->addWidget( slider );
hb->addWidget( new QLabel("Fast", this ) );
hb->addSpacing( 10 );
QPushButton* btn = new QPushButton( "Shuffle Nodes", this );
connect( btn, SIGNAL( clicked() ), this, SLOT( shuffle() ) );
hb->addWidget( btn );
}
GraphWidget::~GraphWidget()
{
delete d;
}
void GraphWidget::setSpeed(int s)
{
d->speed = s;
if ( isVisible() && d->canvas )
d->canvas->setAdvancePeriod( SPEED2ADVANCE( s ) );
}
void GraphWidget::shuffle()
{
for ( NodeItemList::Iterator it = d->nodeItems.begin(); it != d->nodeItems.end(); ++it ) {
NodeItem* ni = (*it);
ni->move(rand()%(d->canvas->width()-ni->width()),rand()%(d->canvas->height()-ni->height()));
}
}
QSize FigureEditor::sizeHint() const
{
return QSize( 600, 400 );
}
void FigureEditor::resizeEvent( QResizeEvent* e )
{
if ( canvas() )
canvas()->resize( contentsRect().width(), contentsRect().height() );
QCanvasView::resizeEvent( e );
}
void FigureEditor::showEvent( QShowEvent* )
{
initialize();
canvas()->setAdvancePeriod( SPEED2ADVANCE(graph->speed) );
}
void FigureEditor::hideEvent( QHideEvent* )
{
initialize();
canvas()->setAdvancePeriod( -10 );
}
void FigureEditor::initialize()
{
if ( canvas() )
return;
resize( sizeHint() );
graph->canvas = new QCanvas( contentsRect().width(), contentsRect().height() );
if ( !tb ) tb = new QBrush( Qt::red );
if ( !tp ) tp = new QPen( Qt::black );
srand( QTime::currentTime().msec() );
int nodecount = 0;
int rows = 3;
int cols = 3;
QMemArray<NodeItem*> lastRow(cols);
for ( int r = 0; r < rows; r++ ) {
NodeItem *prev = 0;
for ( int c = 0; c < cols; c++ ) {
NodeItem *ni = new NodeItem( graph );
ni->setAnimated( TRUE );
nodecount++;
ni->move(rand()%(graph->canvas->width()-ni->width()),rand()%(graph->canvas->height()-ni->height()));
if ( r > 0 )
(new EdgeItem( lastRow[c], ni, graph->canvas ))->show();
if ( prev )
(new EdgeItem( prev, ni, graph->canvas ))->show();
prev = ni;
lastRow[c] = ni;
ni->show();
}
}
graph->canvas->advance();
QCanvasItem* i = new BouncyText( tr( "Drag the nodes around!" ), QFont("helvetica", 24), graph->canvas);
i->show();
setCanvas( graph->canvas );
setMinimumSize( 600, 400 );
setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
}