Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Supported by

Problem with element clicked for arrows


I am implementing a rotation span task and here is the thing.

I have a canvas of arrow in different direction appearing on the screen, during a test part, in which the task of the participant ti to click on the arrow previously presented in a correct order.
Anyway the issue I have right now is that when I try to collect where participants click, I have very odd results. For some reasons, even if they click between arrows, then element will take a value that is close to an arrow. My arrow are black on a white screen this way:

The code I use to generate them is as follow:

import numpy as np

# calculate length total of an arrow,its position at the beginningm and its position at the end
# relatively to the height of the screen
def calc_parameter_arrow(prop_length_arrow, prop_start_point_arrow):
    length_total = int(prop_length_arrow*var.height)
    pos_arrow_begin = int(prop_start_point_arrow*var.height)
    pos_arrow_final_long = length_total + pos_arrow_begin
    return (length_total, pos_arrow_begin, pos_arrow_final_long)

# convert polar to cartesian
def pol2cart(rho, phi):
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)
    return(int(round(x)), int(round(y)))

# transform a length according to various radian, and return a list of list with the x, y cartesian coordinate of this length for these various radians

def coord_cart_radians(length_given, list_radian):
    deg_rad_pos = map(( lambda x: [length_given, x]), list_radian)
    cartesian_pos_return = [pol2cart(p[0], p[1]) for p in deg_rad_pos]

#calculate radian of arrow separated by 45 degrees
nbr_arrow = 8.
degree_sep = 45.
deg = np.arange(nbr_arrow) * degree_sep
var.deg_rad = np.radians(deg)
var.deg_name = [ int(x) for x in deg ]

# various proportion of arrow relative to the height of the screen
var.prop_arrow_long_length_height = 0.3
var.prop_start_point_arrow_long = 0.1

length_arrow_long_total, pos_arrow_begin_long, pos_arrow_final_long = calc_parameter_arrow(var.prop_arrow_long_length_height, var.prop_start_point_arrow_long)

cartesian_pos_arrow_final_long = coord_cart_radians(pos_arrow_final_long, var.deg_rad)
cartesian_pos_arrow_begin_long = coord_cart_radians(pos_arrow_begin_long, var.deg_rad)

Pratice_arrow_long_Instruct_canvas = Canvas()

for pos, (coord_cart_b, coord_cart_f) in enumerate(zip(cartesian_pos_arrow_begin_long, cartesian_pos_arrow_final_long)):    

    Pratice_arrow_long_Instruct_canvas['long_arrow%s' % int(var.deg_name[pos])] = Arrow(coord_cart_b[0],coord_cart_b[1], coord_cart_f[0], coord_cart_f[1], head_width = 50, body_length = 0.8, body_width = 0.5, fill = True, color = 'black')

And in order to lauch the canvas and see my clicked element in the variable inspector:

my_mouse = Mouse(visible=True)
counter = -1

while counter <15:
    button,  (x, y), timestamp = my_mouse.get_click()
    var.element_clicked = Pratice_arrow_long_Instruct_canvas.elements_at(x, y)

And when I click between the arrows, for some reason var.element_clicked is taking the value of closest arrow.
I do not really know what I did wrong.

Thank you for any help I also join an example script.




  • Oh actually I think I noticed something.
    The issue is only happening for arrows that are not in a vertical or horizontal direction.
    Like if all the pixels in a square around the arrows were considered as belonging to it.

  • Hi Sylvain,

    Right now the Canvas.elements_at() indeed checks whether a point falls inside the bounding rectangle of a polygon. Thinking about how to improve this I came across shapely which seems like a good library for this kind of geometrical operations. If you add the following code to the start of the experiment, the arrow and polygons should accurately indicate whether or not a point falls inside them. (This monkey patches the Polygon class.)

    Could you let me know if this works for you?


    from openexp._canvas._polygon.polygon import Polygon
    from shapely.geometry import Polygon as ShapelyPolygon, Point
    def polygon_contains(self, xy):
        if not hasattr(self, '_shapely_polygon'):
            self._shapely_polygon = ShapelyPolygon(self.vertices)   
        return self._shapely_polygon.contains(Point(*xy))
    def polygon_on_attribute_change(self, **kwargs):
        if hasattr(self, '_shapely_polygon'):
            del self._shapely_polygon
    Polygon.__contains__ = polygon_contains
    Polygon.on_attribute_change = polygon_on_attribute_change
  • Hey Sebastiaan,

    Thank you for the answer!
    I installed shapely and indeed adding these lines at the very beginning seems to work very well!
    I know have other issues with arrows (I originally worked on windows, now this week on osx and I encounter odd behavior but I will make another topic I guess).
    But yeah concerning element it works well by adding these lines.

    Thank you!



Sign In or Register to comment.