PDA

View Full Version : reimplementation of collidesWithItem()



MrBeerBear
29th January 2015, 18:48
Hi,

I'm pretty new to Qt (and C++ also) and I hope I'm at the right place to ask this question :)

I'm trying to code a small Billiard game and wanted to reimplement collidesWithItem() as it is suggested in the Qt Documentation.

My problem is, that once I reimplemented the function, it doesn't seem to be called by Qt. It still uses the "default" one. The default one works fine, that's not the problem. It's purely for educational purposes that I'd like to know why it's not called. I did reimplement functions like paint() and they worked perfectly.

Here is my function:


bool Ball::collidesWithItem(const Ball *ball, Qt::ItemSelectionMode mode) const
{
if (this==ball) {return false;}

double epsilon = 100; // I put it this high, so I can see if it works. The Balls change color when colliding.
double distance = sqrt(pow(pos().x()- ball->pos().x(),2) + pow(pos().y()-ball->pos().y(),2));

if(distance-radius-ball->radius < epsilon) {return true;}
else {return false;}
}

Is it linked to the fact that I'm not using QGraphicsItem as the first argument, but Ball? Shouldn't it be overloading in this case?

Here is the Ball.h:


#ifndef BALL_H
#define BALL_H

#include <QGraphicsItem>


class Ball : public QGraphicsItem
{
public:
Ball();
Ball(double x, double y);
Ball(double x, double y, double angle);
~Ball();

QRectF boundingRect() const;
QPainterPath shape() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);

bool collidesWithItem(const Ball *ball, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
qreal getRadius() const;
void setRadius(const qreal &value);

protected:
void advance(int step);

private:
qreal angle;
double position[2];
qreal speed[3];
qreal radius;
qreal mass;
QColor color;

};

#endif // BALL_H

Thank you in advance for your answer :)

stampede
29th January 2015, 19:20
You got it almost right, but you have a signature mismatch. The problem is, your "collidesWithItem" is not a re-implementation of the base class method, you just simply introduced a new function to your class. In order to override a method, it needs to have exactly the same signature as in the base class:


bool QGraphicsItem::collidesWithItem(const QGraphicsItem * other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;
// above signature will work ok, your method:
bool collidesWithItem(const Ball *ball, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;

Btw. if you are using C++11, you can take advantage of the "override" specifier, for example:

#ifndef BALL_H
#define BALL_H

#include <QGraphicsItem>


class Ball : public QGraphicsItem
{
public:
Ball();
Ball(double x, double y);
Ball(double x, double y, double angle);
~Ball();

QRectF boundingRect() const override;
QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget) override;

// this should give a compilation error, as the base class does not declare a virtual method with this signature
bool collidesWithItem(const Ball *ball, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const override;


...

};

#endif // BALL_H

MrBeerBear
29th January 2015, 20:37
Oh ok, I understand. Thank you.

The Problem when I use:


bool QGraphicsItem::collidesWithItem(const QGraphicsItem * other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const;

is that a QGraphicsItem doesn't have a "radius" as member. So when I do


if(distance-radius-other->radius < epsilon) {return true;}
else {return false;}

it gives me obviously a compiler error. How can I circumvent this? Or is there any other way to do this?

wysota
29th January 2015, 20:40
You have to cast the object to your item class. E.g. using qgraphicsitem_cast (see the docs how to use it).

MrBeerBear
3rd February 2015, 14:41
Thanky you wysota, the casting seems to work now. My first problem still remains apparently, I don't know what I'm doing wrong:


#ifndef BALL_H
#define BALL_H

#include <QGraphicsItem>


class Ball : public QGraphicsItem
{
public:
Ball();
Ball(double x, double y);
Ball(double x, double y, double angle);
~Ball();

// Reimplementations
QRectF boundingRect() const;
QPainterPath shape() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
bool collidesWithItem ( const QGraphicsItem * other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape ) const;
int type() const; // for qgraphicsitem_cast

// Setters and Getters
qreal getRadius() const;
void setRadius(const qreal &value);

// for type() reimplementation
enum { Type = UserType + 1 };

protected:
void advance(int step);

private:
qreal angle;
double position[2];
qreal speed[3];
qreal radius;
qreal mass;
QColor color;

};

#endif // BALL_H

and


bool Ball::collidesWithItem( const QGraphicsItem * other, Qt::ItemSelectionMode mode /*= Qt::IntersectsItemShape*/ ) const
{
Ball const* otherBall = qgraphicsitem_cast<Ball const*>(other);
Ball const* thisBall = qgraphicsitem_cast<Ball const*>(this);
std::cout<<"test"<<endl;

double epsilon = 100;
double distance = sqrt(pow(thisBall->pos().x()- otherBall->pos().x(),2) + pow(thisBall->pos().y()-otherBall->pos().y(),2));
if(distance-thisBall->getRadius()-otherBall->getRadius() < epsilon) {return true;}
else {return false;}
}


"collidesWithItem" is still not called somehow, although the signature should be the right one (I even inserted it with the Qt Creator automatically). What am I not seeing? :(

wysota
3rd February 2015, 16:26
You'd have to prepare a minimal compilable example reproducing the problem. Or at least show us how you call collidesWithItem().