Qt-interest Archive, January 2007
QGraphicsSvgItem and collision detection
Message 1 in thread
Hi,
I have an application that uses QGraphicsView and mainly QGraphicsPixmapItem
for the items. Most of the items are created from PNG images that have
transparency. The items are in various shapes and never square shaped.
In the application I want to be able to click in the QGraphicsView and find
the clicked items. I use code like the one below:
void Scene::mousePressEvent (QGraphicsSceneMouseEvent * event) {
QGraphicsItem * clicked = itemAt ( event->scenePos() );
if ( ! clicked ) {
// miss...
return;
}
// a QGraphicsItem was clicked
....
}
This works beautifully for QGraphicsPixmapItem, the itemAt() method accurately
finds the items and correctly leaves out items where I have clicked any
transparent area. This is the expected behaviour.
However, for QGraphicsSvgItem this doesn't work at all. The itemAt() method
seems to check tbe boundingRect() of the item and it's enough to just click
inside it to cause a hit. This is obviously not what I want and makes it
impossible to select items reliably.
So, I tried QGraphicsPixmapItem methods contains(), opaqueArea().contains()
and shape().contains(), but none worked. The elementCount() for shape() is
always 5, which suggests that it simply uses a bounding rectangle, while
elementCount() for opaqueShape() is always 0 which suggests that it does
nothing useful at all?
How can I get a QGraphicsPixmapItem to do any kind of sane collision
detection? There are a few solutions:
1. one solution I've used before is to manually trace coordinates for an
outline for the SVG items, read those into a QPolygon and use that for
collision detection (works fine) but that route is so much work it's not
worth it.
2. manually extract the paths/polygons from the items (they are all one single
filled and closed polygon) and use that for collision detection. There's no
API for this so it can't be done.
3. extract the cached QPixmap from the item and perform collision detection
against its mask. There's no API for this so it can't be done.
My application does work fine with QGraphicsPixmapItem but as my source data
is all SVG and I would like to have the stuff scalable, QGraphicsSvgItem is
the obvious choice. I do assume the SVG stuff in QGraphicsView was meant for
serious use, not just "look mum, I can show an SVG image!" so it must be I
who am too dumb to see what I do wrong.
I'd appreciate any hints and RTFM pointers.
Regards,
Jan Ekholm
--
[ signature omitted ]
Message 2 in thread
Perhaps I had better submit this as a bug to the task tracker? I think it is a
clear bug that QGraphicsPixmapItem and QGraphicsSvgItem work differently when
it comes to collision detection. What do you think?
On Friday 19 January 2007 13:40, Jan Ekholm wrote:
> Hi,
>
> I have an application that uses QGraphicsView and mainly
> QGraphicsPixmapItem for the items. Most of the items are created from PNG
> images that have transparency. The items are in various shapes and never
> square shaped.
>
> In the application I want to be able to click in the QGraphicsView and find
> the clicked items. I use code like the one below:
>
> void Scene::mousePressEvent (QGraphicsSceneMouseEvent * event) {
> QGraphicsItem * clicked = itemAt ( event->scenePos() );
> if ( ! clicked ) {
> // miss...
> return;
> }
>
> // a QGraphicsItem was clicked
> ....
> }
>
> This works beautifully for QGraphicsPixmapItem, the itemAt() method
> accurately finds the items and correctly leaves out items where I have
> clicked any transparent area. This is the expected behaviour.
>
> However, for QGraphicsSvgItem this doesn't work at all. The itemAt() method
> seems to check tbe boundingRect() of the item and it's enough to just click
> inside it to cause a hit. This is obviously not what I want and makes it
> impossible to select items reliably.
>
> So, I tried QGraphicsPixmapItem methods contains(), opaqueArea().contains()
> and shape().contains(), but none worked. The elementCount() for shape() is
> always 5, which suggests that it simply uses a bounding rectangle, while
> elementCount() for opaqueShape() is always 0 which suggests that it does
> nothing useful at all?
>
> How can I get a QGraphicsPixmapItem to do any kind of sane collision
> detection? There are a few solutions:
>
> 1. one solution I've used before is to manually trace coordinates for an
> outline for the SVG items, read those into a QPolygon and use that for
> collision detection (works fine) but that route is so much work it's not
> worth it.
>
> 2. manually extract the paths/polygons from the items (they are all one
> single filled and closed polygon) and use that for collision detection.
> There's no API for this so it can't be done.
>
> 3. extract the cached QPixmap from the item and perform collision detection
> against its mask. There's no API for this so it can't be done.
>
> My application does work fine with QGraphicsPixmapItem but as my source
> data is all SVG and I would like to have the stuff scalable,
> QGraphicsSvgItem is the obvious choice. I do assume the SVG stuff in
> QGraphicsView was meant for serious use, not just "look mum, I can show an
> SVG image!" so it must be I who am too dumb to see what I do wrong.
>
> I'd appreciate any hints and RTFM pointers.
>
> Regards,
> Jan Ekholm
--
[ signature omitted ]
Message 3 in thread
Jan Ekholm wrote:
> Perhaps I had better submit this as a bug to the task tracker? I think it
> is a clear bug that QGraphicsPixmapItem and QGraphicsSvgItem work
> differently when it comes to collision detection. What do you think?
Because QGraphicsSvgItem's collision detection requires boolean painter path
operations, and Qt 4.2 does not support that, we cannot provide automatic
collision detection for items beyond their bounding rect. You can write
your own shape() implementation and return a QPainterPath that resembles
your item's shape to work around it.
You could say it's a bug or a missing feature, and we are working on it. You
could submit it to the task tracker, if only so that you and others can
search it up and follow progress.
Andreas
--
[ signature omitted ]
Message 4 in thread
On Monday 22 January 2007 09:57, Andreas Aardal Hanssen wrote:
> Jan Ekholm wrote:
> > Perhaps I had better submit this as a bug to the task tracker? I think it
> > is a clear bug that QGraphicsPixmapItem and QGraphicsSvgItem work
> > differently when it comes to collision detection. What do you think?
>
> Because QGraphicsSvgItem's collision detection requires boolean painter
> path operations, and Qt 4.2 does not support that, we cannot provide
> automatic collision detection for items beyond their bounding rect. You can
> write your own shape() implementation and return a QPainterPath that
> resembles your item's shape to work around it.
Well, it I got access to the cached pixmap it would be doable, or at least I
could create my own collision detection. Right now the only way to create an
own shape() is to read the same SVG file manually and try to understand it in
code. Another is to precreate PNG versions of the SVG images, but that
somehow makes the whole idea of using SVG redundant.
> You could say it's a bug or a missing feature, and we are working on it.
> You could submit it to the task tracker, if only so that you and others can
> search it up and follow progress.
Oh, features are always missing and always will be. A small note could be
added to the QGraphicsSvgItem docs to indicate that the item doesn't behave
like other items wrt collision detection. I spent a few hours hunting bugs in
my code until I discovered that.
However, for now I simply store an additional QImage along with the item and
use it for collision detection. Something like:
QPixmap image = QPixmap ( boundingRect().size().toSize() );
image.fill ( QColor(0,0,0,0));
QPainter painter ( &image );
// render the SVG into the image
renderer()->render ( &painter );
painter.end();
m_mask = image.toImage();
Collisions can then be checked with:
//
QPoint point = ...
// get the actual hit pixel and convert to a RGBA color
QColor hit_pixel =
QColor::fromRgba ( m_mask.pixel(point.x(), point.y()) );
// it is hit if the alpha is non 0
return hit_pixel.alpha() != 0;
Yes, this wastes some memory, but as this is a game it's allowed to
shamelessly waste resources. :)
I also noticed that the only way to assign a SVG file to a QGraphicsSvgItem is
by passing the filename in the constructor. There is AFAIK no way to do it
once the item is created. I tried to use this code which looks quite logical
to me, but which didn't load anything at all (nor give an error):
if ( ! renderer()->load ("file.svg") ) {
// failed...
}
It gives an empty item. The renderer is not changed nor shared, it's the
item's default renderer.
Anyway, with some careful navigation around the mines in my path I seem to be
able to use SVG with QGraphicsView. Some updates in the docs would sweep away
quite a few of the mines though...
--
[ signature omitted ]
Message 5 in thread
Jan Ekholm wrote:
> Yes, this wastes some memory, but as this is a game it's allowed to
> shamelessly waste resources. :)
We can't use something like this in Qt. :-)
> It gives an empty item. The renderer is not changed nor shared, it's the
> item's default renderer.
If you found a bug, please post it to the public task tracker; that way, you
can confirm if it is a bug or not, and the right people will know about it.
Andreas
--
[ signature omitted ]
Message 6 in thread
On Monday 22 January 2007 11:08, Andreas Aardal Hanssen wrote:
> Jan Ekholm wrote:
> > Yes, this wastes some memory, but as this is a game it's allowed to
> > shamelessly waste resources. :)
>
> We can't use something like this in Qt. :-)
Obviously. But you can document why the item types work differently and what
will not work with SVG items. However, I rather have a temporary solution
that wastes memory and works than one that is supposed to work (according to
the docs) but doesn't.
> > It gives an empty item. The renderer is not changed nor shared, it's the
> > item's default renderer.
>
> If you found a bug, please post it to the public task tracker; that way,
> you can confirm if it is a bug or not, and the right people will know about
> it.
I'm very reluctant to do that. Last time I tried it the whole task tracker
didn't even work with Konqueror. In this case it's also probably something
that I do wrong, I'm a newbie.
--
[ signature omitted ]
Message 7 in thread
Jan Ekholm wrote:
>> We can't use something like this in Qt. :-)
> Obviously. But you can document why the item types work differently and
We'll see what happens.
>> If you found a bug, please post it to the public task tracker; that way,
> I'm very reluctant to do that. Last time I tried it the whole task tracker
If you don't post the bug because of a task tracker problem with Konqueror,
then there's a chance your bug will not get fixed. Nobody wins :-).
Andreas
--
[ signature omitted ]