Source code for expyriment.misc.geometry

"""
The geometry module.

This module contains miscellaneous geometry functions for expyriment.

"""

__author__ = 'Florian Krause <florian@expyriment.org>, \
Oliver Lindemann <oliver@expyriment.org>'
__version__ = '0.6.2+'
__revision__ = 'df3f5391d9d2+'
__date__ = 'Sun Apr 14 12:55:36 2013 +0200'

import math as _math
import expyriment as _expyriment


[docs]def coordinates2position(coordinate): """Convert a coordinate on the screen to an expyriment position. Parameters ---------- coordinate : (int, int) coordinate (x,y) to convert Returns ------- coordinate : (int, int) """ screen_size = _expyriment._active_exp.screen.surface.get_size() return (coordinate[0] - screen_size[0] / 2, - coordinate[1] + screen_size[1] / 2)
[docs]def position2coordinate(position): """Convert an expyriment position to a coordinate on screen. Parameters ---------- coordinate : (int, int) coordinate (x,y) to convert Returns ------- coordinate : (int, int) """ screen_size = _expyriment._active_exp.screen.surface.get_size() return (position[0] + screen_size[0] / 2, - position[1] + screen_size[1] / 2)
[docs]def position2visual_angle(position, viewing_distance, monitor_size): """Convert an expyriment position (pixel) to a visual angle from center. Parameters ---------- position : (int, int) position (x,y) to convert viewing_distance : numeric viewing distance in cm monitior_size : numeric physical size of the monitor in cm (x, y) Returns ------- angle : (float, float) visual angle for x & y dimension """ screen_size = _expyriment._active_exp.screen.surface.get_size() cm = (position[0] * monitor_size[0] / float(screen_size[0]), position[1] * monitor_size[1] / float(screen_size[1])) angle = (2.0 * _math.atan((cm[0] / 2) / viewing_distance), 2.0 * _math.atan((cm[1] / 2) / viewing_distance)) return (angle[0] * 180 / _math.pi, angle[1] * 180 / _math.pi)
[docs]def visual_angle2position(visual_angle, viewing_distance, monitor_size): """Convert an position defined as visual angle from center to expyriment position (pixel). Parameters ---------- visual_angle : (numeric, numeric) position in visual angle (x,y) to convert viewing_distance : numeric viewing distance in cm monitior_size : (numeric, numeric) physical size of the monitor in cm (x, y) Returns ------- position : (float, float) position (x,y) """ screen_size = _expyriment._active_exp.screen.surface.get_size() angle = (visual_angle[0] * _math.pi / 360, visual_angle[1] * _math.pi / 360) # angle / 180 / 2 cm = (_math.tan(angle[0]) * viewing_distance * 2, _math.tan(angle[1]) * viewing_distance * 2) return (cm[0] * screen_size[0] / monitor_size[0], cm[1] * screen_size[1] / monitor_size[1])
[docs]def points_to_vertices(points): """Returns vertex representation of the points (int, int) in xy-coordinates Parameters ---------- points : (int, int) list of points Returns ------- vtx : list list of vertices """ vtx = [] for i in range(1, len(points)): vtx.append((points[i][0] - points[i - 1][0], points[i][1] - points[i - 1][1])) return vtx
[docs]def lines_intersect(pa, pb, pc, pd): """Return true if two line segments are intersecting Parameters ---------- pa : misc.XYPoint point 1 of line 1 pb : misc.XYPoint point 2 of line 1 pc : misc.XYPoint point 1 of line 2 pb : misc.XYPoint point 2 of line 2 Returns ------- check : bool True if lines intersect """ def ccw(pa, pb, pc): return (pc._y - pa._y) * (pb._x - pa._x) > (pb._y - pa._y) * (pc._x - pa._x) return ccw(pa, pc, pd) != ccw(pb, pc, pd) and ccw(pa, pb, pc) != ccw(pa, pb, pd)
[docs]class XYPoint: """ The Expyriment point class """
[docs] def __init__(self, x=None, y=None, xy=None): """Initialize a XYPoint. Parameters ---------- x : numeric y : numeric xy : (numeric, numeric) xy = (x,y) Notes ----- use `x`, `y` values (two numberic) or the tuple xy=(x,y) """ if x is None: if xy is None: self._x = 0 self._y = 0 else: self._x = xy[0] self._y = xy[1] elif y is None: #if only a tuple is specified: e-g. Point((23,23)) self._x = x[0] self._y = x[1] else: self._x = x self._y = y
def __repr__(self): return "(x={0}, y={1})".format(self._x, self._y) @property def x(self): """Getter for x""" return self._x @x.setter
[docs] def x(self, value): """Getter for x""" self._x = value
@property def y(self): """Getter for y""" return self._y @y.setter
[docs] def y(self, value): """Getter for y""" self._y = value
@property def tuple(self): return (self._x, self._y) @tuple.setter
[docs] def tuple(self, xy_tuple): self._x = xy_tuple[0] self._y = xy_tuple[1]
[docs] def move(self, v): """Move the point along the coodinates specified by the vector v. Parameters ---------- v : misc.XYPoint movement vector """ self._x = self._x + v._x self._y = self._y + v._y return self
[docs] def distance(self, p): """Return euclidian distance to the points (p). Parameters ---------- p : misc.XYPoint movement vector Returns ------- dist : float distance to other point p """ dx = self._x - p._x dy = self._y - p._y return _math.sqrt((dx * dx) + (dy * dy))
[docs] def rotate(self, degree, rotation_centre=(0, 0)): """Rotate the point counterclockwise in degree around rotation_centre. Parameters ---------- degree : int degree of rotation (default=(0, 0) ) rotation_center : (numeric, numeric) rotation center (x, y) """ p = XYPoint(self._x - rotation_centre[0], self._y - rotation_centre[1]) #cart -> polar ang = _math.atan2(p._x, p._y) r = _math.sqrt((p._x * p._x) + (p._y * p._y)) ang = ang - ((degree / 180.0) * _math.pi); #polar -> cart self._x = r * _math.sin(ang) + rotation_centre[0] self._y = r * _math.cos(ang) + rotation_centre[1] return self
[docs] def is_inside_polygon(self, point_list): """Return true if point is inside a given polygon. Parameters ---------- point_list : list point list defining the polygon Returns ------- check : bool """ n = len(point_list) inside = False p1 = point_list[0] for i in range(n + 1): p2 = point_list[i % n] if self._y > min(p1._y, p2._y): if self._y <= max(p1._y, p2._y): if self._x <= max(p1._x, p2._x): if p1._y != p2._y: xinters = (self._y - p1._y) * (p2._x - p1._x) / (p2._y - p1._y) + p1._x if p1._x == p2._x or self._x <= xinters: inside = not inside p1 = p2 return inside