Circle, Rect and Shape components for Builder

655 views
Skip to first unread message

Hiroyuki Sogo

unread,
Oct 1, 2012, 8:53:58 AM10/1/12
to psycho...@googlegroups.com
Hi,

I added three components (Circle, Rect and Shape) to Builder.
( https://github.com/hsogo/psychopy/tree/builder-newcomponents )
They present a circle, rectangle and polygon on Builder.
Following parameters can be updated during presentation.
- Fill color
- Line color
- opacity
- orientation
- position
- size
- radius (Circle only)
- width (Rect only)
- height (Rect only)
- vertices (Shape only)

They seem to work properly as far as I have tested.

Six files (circle.py, circle.png, rect.py, rect.png, shape.py and shape.png)
were newly appended to the 'components' directory.  components/__init__.py
and components/_visual.py were modified to support newly appended
parameters, 'fillColor', 'lineColor', 'fillColorSpace', 'lineColorSpace', 'radius',
'width', 'height' and 'vertices'.

Could you review these codes?

Thank you.

--
Hiroyuki

Jonathan Peirce

unread,
Oct 1, 2012, 12:33:33 PM10/1/12
to psycho...@googlegroups.com
Thanks for getting onto this. I was wondering about a general Shape
Component that had a shape parameter with possible values =
['line','triangle','square','circle']. This would be passed to the
ShapeStim on its creation. For the user it means only one extra
component button (showing a triangle, circle and square in its icon?)
and for the developer it means fewer files to change if you want to
alter something. What do you think?

I think, in the code, a triangle can be called with either of;
visual.Triangle(win....)
visual.Polygon(win, edges=3, radius=[w,h],...)
and a circle is, essentially;
visual.Polygon(win, edges=32, radius=[w,h],...) #or more edges if
you want a more accurate circle

For a circle, although radius makes most sense as a term, the fact that
width and height allow you to create ellipses seems quite nice, and
makes it similar to the other stimuli.

For a custom set of vertices the easiest way to implement it is
certainly then to have a CustomShape Component. Maybe put that one in
the Custom category rather than Stimuli(?)

thanks again,
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.

Jeremy Gray

unread,
Oct 1, 2012, 1:05:07 PM10/1/12
to psycho...@googlegroups.com

Thanks for getting onto this. I was wondering about a general Shape Component that had a shape parameter with possible values = ['line','triangle','square','circle']. This would be passed to the ShapeStim on its creation.

This is how visual.ApertureStim() handles shapes--you can request simple shapes and it figures out how to implement them.

--Jeremy

Hiroyuki Sogo

unread,
Oct 2, 2012, 4:07:02 AM10/2/12
to psycho...@googlegroups.com
General shape component sounds nice.
I tried writing a new Shape component that supports line, triangle, square and circle.
Previous Shape component was renamed to CustomShape.
Rect and Circle components have not been deleted yet.
I've committed these changes to my branch. Please review them.

The new shape component has 'height' option (labelled 'Size' in the Builder dialog).
If the shape is 'line', 'height' parameter corresponds to the length of the line.
For other shapes, 'height' parameter corresponds to the height of the shape.

By default, a vertical line is drawn when shape option is 'line'.
Setting 'ori' option is necessary to draw oblique or horizontal lines.
I think that parameters required for drawing lines are different from those for
drawing other shapes.  It may be better to make a separate component to
draw lines.

I didn't use psychopy.visual.Circle(win, edge=3, ...) to draw triangle because
I want to align the bottom of a triangle and a rectangle when their vertical position
and height are the same.  To achieve this, the origin the triangle is at the vertical
center of the triangle. If psychopy.visual.Circle is used to draw a triangle,
the origin of the triangle is at the center of gravity of the rectangle.
Which do you think better?

--
Hiroyuki


Jeremy Gray

unread,
Oct 2, 2012, 7:36:36 AM10/2/12
to psycho...@googlegroups.com
Hi Hiroyuki,

I haven't tried this out yet but I think its looking promising. 

I like the way you handle triangles now (as a ShapeStim). People will be able to see the code that is generated, and it would be mildly confusing to see myTriangle = Circle(win, ....)

You might also consider:
- What happens to circles at a large size? I suspect that 32 edges will start to be noticeable as edges. Maybe 64 or 128 might be good for any scale and should not be effectively any slower to draw?

- This would be more effort, and its just an idea: Currently everything is static (set at init time), but it would be possible to make things adjustable per frame or repeat (= dynamic) by having code for this in writeRoutineStartCode and writeFrameCode. For pos, you'd need a param of val-type code, and would call .setPos(). size would work too. also: lineColor, fillColor, opacity, ... its a long list but might be nice. For example, this would allow people to do things like change the color if the mouse is hovering over the shape, or if they clicked inside the shape. It should be possible for people to do things like this already, using a code component, but would not be as obvious to people that such a thing is possible. You'll probably have to limit the options for this, otherwise the pop-up param menu will get very cluttered.

Nice work so far,

--Jeremy

Jonathan Peirce

unread,
Oct 2, 2012, 9:19:05 AM10/2/12
to psycho...@googlegroups.com


On 02/10/2012 09:07, Hiroyuki Sogo wrote:
>
> I didn't use psychopy.visual.Circle(win, edge=3, ...) to draw triangle
> because
> I want to align the bottom of a triangle and a rectangle when their
> vertical position
> and height are the same. To achieve this, the origin the triangle is
> at the vertical
> center of the triangle. If psychopy.visual.Circle is used to draw a
> triangle,
> the origin of the triangle is at the center of gravity of the rectangle.
> Which do you think better?
>
> --
> Hiroyuki
>
>
All those things sound fine. I'll try and have a play with your work
later in the week. I see what you're saying about the triangle, and I
agree it would be nice to align the bottom edge with the equivalent
square. Sounds good.

Hiroyuki Sogo

unread,
Oct 3, 2012, 1:35:17 AM10/3/12
to psycho...@googlegroups.com
Thank you for your comment.
I increased number of edges from 32 to 64.

My shape component has already support both 'set every repeat' and 'set every frame'
by inheriting writeRoutineStartCode and writeFrameCode from VisualComponent class.
So, adjusting parameters per repeat is already possible by using a conditions file.
On the other hand, as you said, using a code component is necessary to change
parameters in accordance with mouse motion.  I will try to implement this feature,
but it will take time.

--
Hiroyuki

Hiroyuki Sogo

unread,
Oct 12, 2012, 5:24:49 AM10/12/12
to psycho...@googlegroups.com
Hi,

I added options to ShapeComponent to enable changing color and opacity of the shape when mouse cursor is on the shape and/or the left button of the mouse is clicked on the shape. I didn't support changing size and orientation because whether mouse cursor is on the shape may alter frame by frame if these parameters are changed.  I uploaded sample psyexp files to github:gist.

https://gist.github.com/3878085
https://gist.github.com/3824878

In addition, I modified visual.ShapeStim.setPos() to accept event.Mouse object as a new position.  With this change, users can move position of a shape stimulus by using mouse simply setting name of a MouseComponent as position of ShapeComponent.  I wonder whether this modification may be unnecessary because this can be realized without modifying codes (i.e., by setting 'mouse.getPos()' as position of ShapeComponent if name of the MouseComponet is 'mouse'). However, I think that 'mouse' may be easier to understand compared to 'mouse.getPos()' especially for beginners. What do you think?

--
Hiroyuki

Hiroyuki Sogo

unread,
Dec 25, 2012, 7:41:54 AM12/25/12
to psycho...@googlegroups.com
2012年10月2日火曜日 20時36分37秒 UTC+9 Jeremy Gray:
- This would be more effort, and its just an idea: Currently everything is static (set at init time), but it would be possible to make things adjustable per frame or repeat (= dynamic) by having code for this in writeRoutineStartCode and writeFrameCode. For pos, you'd need a param of val-type code, and would call .setPos(). size would work too. also: lineColor, fillColor, opacity, ... its a long list but might be nice. For example, this would allow people to do things like change the color if the mouse is hovering over the shape, or if they clicked inside the shape. It should be possible for people to do things like this already, using a code component, but would not be as obvious to people that such a thing is possible. You'll probably have to limit the options for this, otherwise the pop-up param menu will get very cluttered.

I'm sorry for late response.  I tried to add parameters (that is, adding psychopy.app.builder.experiment.Param objects) to ShapeComponent to implement such dynamic updating, but the result was not satisfactory. Adding a few parameters to ShapeComponent makes "Propertires" dialog so tall that whole part of the dialog cannot be shown in a low-resolution (e.g. XGA) display. It was difficult to implement practically useful dynamic updating functions by adding only a few parameters. So, as a trial, I modified writeInitCode(), writeRoutineStartCode() and writeParamUpdates() methods to accept multiple values in a single parameter. I'm not sure whether this extension will be prefered by users, but this extension enables dynamic updating without adding new parameters.


Please extract "__init__.py", "shape.py" and "shape.png" from an attached zip file and copy them to psychopy/app/builder/components folder. ShapeComponent defined in "shape.py" accepts a list in following format as the value of "fillColor", "lineColor", "lineWidth", "ori", "pos", "size" and "opacity" when "updates" is set "every frame".

[[condigion_1, value1], [condition2, value2], ... , ['default', value_N]]

If the value is not matched to this format, the value is evaluated in the same way as that in the other Builder component.

Following strings can be used as "condition". The last condition must be "default". If multiple conditions are given, the conditions are evaluated from left to right.

* mouse#hover       : set value if mouse cursor is on the shape.
* mouse#pressed#N   : set value if mouse button is pressed. (N=0,1,2)
* mouse#pressedOn#N : set value if mouse button is pressed on the shape (N=0,1,2)
* mouse#clicked#N   : set value if mouse button is clicked. (N=0,1,2)
* mouse#clickedOn#N : set value if mouse button is clicked on the shape. (N=0,1,2)
* key#clicked#NAME  : set value if key is pressed. (NAME=key name)
* contains#NAME     : set value if the shape contains an object named "NAME".
* overlaps#NAME     : set value if the shape overlaps with an object named "NAME".
* default           : default value.

For example, following list means that the value is [1,1,1] if left mouse button is pressed on the shape. If left mouse button is not pressed and the mouse cursor is on the shape, the value is 'red'. Otherwise, the value is (-1,-1,-1).

$[['mouse#pressedOn#0',[1,1,1]],['mouse#hover','red'],['default',(-1,-1,1)]]

If this list is set to 'fillColor' property of ShapeComponent object named 'shape', this list is compiled as following.

    # *shape* updates
   
    # (snip)
   
    if shape.status == STARTED:#only update if being drawn
        if shape.contains(mouse) and mouse.getPressed()[0]:
            shape.setFillColor([1,1,1], colorSpace=u'rgb', log=False)
        elif shape.contains(mouse):
            shape.setFillColor('red', colorSpace=u'rgb', log=False)
        else:
            shape.setFillColor((-1,-1,1), colorSpace=u'rgb', log=False)

Please see attached files (shape-keytest.psyexp, shape-mouseteset.psyexp) for example of this extension.


Again, I'm not sure whether this extension will be prefered by users. If members of psychopy-dev think this extension is undesirable, I'll abandon this approach. I would appreciate your comments and suggestions.

Thank you.

--
Hiroyuki Sogo


dynamic_shape.zip
Reply all
Reply to author
Forward
0 new messages