visual.ShapeStim.contains() returns wrong value in some conditions

35 views
Skip to first unread message

Hiroyuki Sogo

unread,
Oct 20, 2012, 2:12:38 AM10/20/12
to psycho...@googlegroups.com
Hi all,

I noticed that visual.ShapeStim.contains() returns wrong value in some conditions (PsychoPy version=1.75.01).
Attached scripts are demonstrations for this problem.
These scripts examine whether a rectangle contains the center of a yellow dot using contains() method.
When 'units' is 'deg' or 'cm', contains() returns True in spite that the yellow dot is outside of the rectangle (contains_unit_test.py).
In addition, contains() ignores 'ori' option (contains_ori_test.py).

To fix these bugs, I modified visual._BaseVisualStim.contains (line 1388-1394 of visual.py) as following.

        if self.needVertexUpdate:
            self._calcVerticesRendered()
        if hasattr(x, 'getPos'):
            x0, y0 = x.getPos()
        elif type(x) in [list, tuple, numpy.ndarray]:
            x0, y0 = x[0], x[1]
        else:
            (x0, y0) = (x, y)
        if self.units in ['deg','degs']:
            x0, y0 = psychopy.misc.deg2pix(numpy.array((x0,y0)), self.win.monitor)
        elif self.units == 'cm':
            x0, y0 = psychopy.misc.cm2pix(numpy.array((x0,y0)), self.win.monitor)
       
        x = numpy.cos(self.ori/180.0*numpy.pi)*x0 - numpy.sin(self.ori/180.0*numpy.pi)*y0
        y = numpy.sin(self.ori/180.0*numpy.pi)*x0 + numpy.cos(self.ori/180.0*numpy.pi)*y0
       
        return pointInPolygon(x, y, self)

At first, values passed to contains() are converted to pix when self.units is 'deg', 'degs' or 'cm'.
Then, values are rotated using self.ori.

Applying these modifications, contains() returns correct value.
Please review these codes.

Thank you.

--
Hiroyuki

contains_ori_test.py
contains_unit_test.py

Jeremy Gray

unread,
Oct 20, 2012, 10:51:35 AM10/20/12
to psycho...@googlegroups.com
Hi Hiroyuki,

This looks like a worthwhile and useful extension. I have not yet
tried the code (I presume it works as you describe, of course). A
couple comments:

Because this code can be useful to call within a drawing loop, its
important for it to be as efficient as possible. So I would write the
last part like this (code not tested, but you'll see the idea):
if self.ori:
oriRadians = numpy.radians(self.ori)
sinOri = numpy.sin(oriRadians)
cosOri = numpy.cos(oriRadians)
x = x0 * cosOri - y0 * sinOri
y = x0 * sinOri + y0 * cosOri
else:
x, y = x0, y0

To extend your fix to .overlaps(), I think you'll want to do the same
thing conceptually, but transforming a whole vector of points at once.
The above code might work for that (as part of a solution). I put x0 *
cosOri (in that order), hoping that a vector, x0, might work. Then
pass the lists of re-oriented points to polygonsOverlap().

Finally, I like your tests a lot. It would be great if these could be
automated, and added to the test suite. So in addition to having a
param list for units, you could have another list of points, chosen to
cover all the possibilities (outside both shapes, inside one, inside
the other, inside both). Then test whether each shape contains each
point. If any of the points gives the wrong answer, raise an error.

--Jeremy

Jonathan Peirce

unread,
Oct 22, 2012, 4:21:48 AM10/22/12
to psycho...@googlegroups.com
Agreed, looks great Hiroyuki. Submit a pull request when you get time
and it will be in the next release.
Thanks,
Jon
--
Jonathan Peirce
Nottingham Visual Neuroscience

http://www.peirce.org.uk


This message and any attachment are intended solely for the addressee and may contain confidential information. If you have received this message in error, please send it back to me, and immediately delete it. Please do not use, copy or disclose the information contained in this message or in any attachment. Any views or opinions expressed by the author of this email do not necessarily reflect the views of the University of Nottingham.

This message has been checked for viruses but the contents of an attachment
may still contain software viruses which could damage your computer system:
you are advised to perform your own checks. Email communications with the
University of Nottingham may be monitored as permitted by UK legislation.

Hiroyuki Sogo

unread,
Oct 22, 2012, 7:23:56 AM10/22/12
to psycho...@googlegroups.com
Thank you for your comments!
I've submitted a pull request.

By the way, I tried writing an automated test of contains() and overlaps().
Attached script draws 6 points and a rectangle on the screen and tests
whether the rectangle contains these points changing orientation,
size and position of the rectangle. If an unexpected value is returned,
the script raises an error.

--
Hiroyuki

test_contains_overlaps.py

Jonathan Peirce

unread,
Nov 8, 2012, 1:09:35 PM11/8/12
to psycho...@googlegroups.com
Thanks Hiroyuki,

I've added that to the psychopy test suite (only slightly modified to
run under pytest/nose) so it will now be part of all standard testing:
https://github.com/psychopy/psychopy/commit/80ae6291f7c42a7de476273a2920a8eb0f22b058

Jon

Sogo Hiroyuki

unread,
Nov 9, 2012, 4:12:57 AM11/9/12
to psycho...@googlegroups.com
Hi Jon,

I'm glad to hear that.
Thank you!

--
Hiroyuki

2012/11/9 Jonathan Peirce <jonatha...@nottingham.ac.uk>
Reply all
Reply to author
Forward
0 new messages