PageRenderTime 34ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/psychopy/iohub/devices/display/unit_conversions.py

https://gitlab.com/braintech/psychopy-brain
Python | 301 lines | 66 code | 35 blank | 200 comment | 2 complexity | 834e619827fcefeef2352e337511776a MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. # Part of the psychopy.iohub library.
  3. # Copyright (C) 2012-2013 Josh Borah
  4. # Copyright (C) 2012-2016 iSolver Software Solutions
  5. # Distributed under the terms of the GNU General Public License (GPL).
  6. #
  7. # fileauthor:: Sol Simpson <sol@isolver-software.com>
  8. # fileauthor:: Josh Borah <josh@a-s-l.com>
  9. from math import atan, tan, sqrt
  10. #
  11. # distToPixel
  12. #
  13. # Convert between distance coordinates and pixel coordinates.
  14. #
  15. # Distance coordinates are 2D Cartesian coordinates, measured from an origin at the
  16. # center pixel, and are real distance units (inches, centimeters, etc.) along horizontal and
  17. # vertical screen axes.
  18. #
  19. def distToPixel(
  20. hpix_per_dist_unit,
  21. vpix_per_dist_unit,
  22. pixHres,
  23. pixVres,
  24. distH,
  25. distV):
  26. pixH = pixHres / 2.0 + (distH * hpix_per_dist_unit)
  27. pixV = pixVres / 2.0 + (distV * vpix_per_dist_unit)
  28. return pixH, pixV
  29. def pixelToDist(
  30. hpix_per_dist_unit,
  31. vpix_per_dist_unit,
  32. pixHres,
  33. pixVres,
  34. pixH,
  35. pixV):
  36. distH = (pixH - pixHres / 2.0) / hpix_per_dist_unit
  37. distV = (pixV - pixVres / 2.0) / vpix_per_dist_unit
  38. return distH, distV
  39. #
  40. # All of following assume a nominal eye point 'eye2display' distance units from display
  41. # with line-of-gaze normal to the display at the display center. Angle variable are
  42. # assumed to have units of degrees.
  43. #
  44. # Since the Python math lib trig functions work with radians,
  45. # a radian to angle conversion factor (deg/rad = 57.2958) is included to give angle
  46. # variables 'degree' units.
  47. #
  48. #
  49. # Convert between distance coordinates (distH, distV) and 'normalized Cartesian
  50. # coordinates' (ndH, ndV).
  51. #
  52. # 'Normalized Cartesian coordinates' are Cartesian distance coordinates, normalized by
  53. # by the distance from the nominal eye point to the display. For very small distances
  54. # from the origin, these values coorespond to visual angle from the origin along the
  55. # horizontal and vertical display axes. A factor of 57.2958 is used so that the values
  56. # correspond to degrees rather than radians.
  57. #
  58. def convertDistToNd(eye2display, distH, distV):
  59. ndH = 57.2958 * distH / eye2display
  60. ndV = 57.2958 * distV / eye2display
  61. return ndH, ndV
  62. def convertNdToDist(eye2display, ndH, ndV):
  63. distH = ndH * eye2display / 57.2958
  64. distV = ndV * eye2display / 57.2958
  65. return distH, distV
  66. #
  67. # Convert between distance coordinates (distH, distV) and
  68. # 'Cartesian Angles' (caH, caV).
  69. # 'Cartesian Angles' are visual angles (from nominal eye point) along
  70. # horizontal and vertical display axes. In other words, the horizontal coordinate is the
  71. # visual angle between the origin and the intersection of the Cartesian
  72. # coordinate line with the horizontal axes.
  73. #
  74. def distToCa(eye2display, distH, distV):
  75. caH = 57.2958 * atan(distH / eye2display)
  76. caV = 57.2958 * atan(distV / eye2display)
  77. return caH, caV
  78. def caToDist(eye2display, caH, caV):
  79. distH = eye2display * tan(caH / 57.2958)
  80. distV = eye2display * tan(caV / 57.2968)
  81. return distH, distV
  82. #
  83. # Convert between distance coordinates (distH, distV) and Fick Coordinates (as,el)
  84. #
  85. def distToFick(eye2display, distH, distV):
  86. az = 57.2958 * atan(distH / eye2display)
  87. el = 57.2958 * atan(distV / sqrt(eye2display *
  88. eye2display + distH * distH))
  89. return az, el
  90. def fickToDist(eye2display, az, el):
  91. distH = eye2display * tan(az / 57.2958)
  92. distV = sqrt(eye2display * eye2display + distH * distH) * tan(el / 57.2958)
  93. return distH / distV
  94. #
  95. # Convert between distance coordinates (distH, distV) and 'symmetric angle'
  96. # coordinates (saH, saV).
  97. # 'Symmetric angles' are visual angles between a point on the display and the central
  98. # axes lines, measured along lines parallel to the display axes. The vertical coordinate is
  99. # same as the Fick elevation angle. The horizontal coordinate is measured in a
  100. # symmetrical fashion and is not the same as the Fick azimuth angle.
  101. #
  102. def distToSa(eye2display, distH, distV):
  103. saH = 57.2958 * atan(distH / sqrt(eye2display *
  104. eye2display + distV * distV))
  105. saV = 57.2958 * atan(distV / sqrt(eye2display *
  106. eye2display + distH * distH))
  107. return saH, saV
  108. def saToDist(eye2dsply, saH, saV):
  109. tansaV_sqrd = tan(saV / 57.2958) * tan(saV / 57.2958)
  110. tansaH_sqrd = tan(saH / 57.2958) * tan(saH / 57.2958)
  111. Dsqrd = eye2dsply * eye2dsply
  112. signsaV = 1.0
  113. if saV < 0.0:
  114. signsaV = -1.0
  115. signsaH = 1.0
  116. if saH < 0.0:
  117. signsaH = -1.0
  118. distV = signsaV * sqrt((Dsqrd * tansaV_sqrd + Dsqrd *
  119. tansaH_sqrd * tansaV_sqrd) / (1 - tansaH_sqrd * tansaV_sqrd))
  120. distH = signsaH * sqrt((Dsqrd + distV * distV) * tansaH_sqrd)
  121. return distV, distH
  122. #-------------------------------------
  123. # Old code using matrix multiplication to convert between screen pix and
  124. # experiment software coord system...
  125. # if coord_type=='pix':
  126. # origin=self.getOrigin()
  127. # if origin not in Display._supported_origin_types:
  128. # print2err(" *** Display device error: Unknown origin type: {0}".format(origin))
  129. # return
  130. #
  131. # x1,y1,x2,y2=self.getBounds()
  132. # print2err('getBounds: ',self.getBounds(), )
  133. #
  134. # bounds_matrix=np.matrix([[x1,y1,1,0],[-y1,x1,0,1],[x2,y2,1,0],[-y2,x2,0,1]])
  135. #
  136. # cx1=None
  137. # cy1=None
  138. # cx2=None
  139. # cy2=None
  140. #
  141. ## if coord_type == 'org':
  142. ## cx1=x1
  143. ## cy1=y1
  144. ## cx2=x2
  145. ## cy2=y2
  146. # if coord_type == 'pix':
  147. # if origin == 'center':
  148. # cx1=-pixel_width/2.0
  149. # cy1=pixel_height/2.0
  150. # cx2=pixel_width/2.0
  151. # cy2=-pixel_height/2.0
  152. ## elif origin == 'top_left':
  153. ## cx1=0
  154. ## cy1=0
  155. ## cx2=pixel_width
  156. ## cy2=pixel_height
  157. ## elif origin == 'bottom_left':
  158. ## cx1=0
  159. ## cy1=pixel_height
  160. ## cx2=pixel_width
  161. ## cy2=0
  162. # elif coord_type in ['mm','cm','inch']:
  163. # phys_to_coord_ratio=1.0
  164. ## if coord_type == 'mm':
  165. ## if phys_unit_type == 'cm':
  166. ## phys_to_coord_ratio=10.0
  167. ## elif phys_unit_type == 'inch':
  168. ## phys_to_coord_ratio=25.4
  169. # if coord_type == 'cm':
  170. # if phys_unit_type == 'mm':
  171. # phys_to_coord_ratio=0.1
  172. # elif phys_unit_type == 'inch':
  173. # phys_to_coord_ratio=2.54
  174. ## elif coord_type == 'inch':
  175. ## if phys_unit_type == 'mm':
  176. ## phys_to_coord_ratio=0.0393701
  177. ## elif phys_unit_type == 'cm':
  178. ## phys_to_coord_ratio=0.393701
  179. #
  180. # if origin == 'center':
  181. # phys_to_coord_ratio=2.0*phys_to_coord_ratio
  182. # cx1=-phys_width/phys_to_coord_ratio
  183. # cy1=phys_height/phys_to_coord_ratio
  184. # cx2=phys_width/phys_to_coord_ratio
  185. # cy2=-phys_height/phys_to_coord_ratio
  186. ## elif origin == 'top_left':
  187. ## cx1=0.0
  188. ## cy1=0.0
  189. ## cx2=phys_width*phys_to_coord_ratio
  190. ## cy2=phys_height*phys_to_coord_ratio
  191. ## elif origin == 'bottom_left':
  192. ## cx1=0.0
  193. ## cy1=phys_height*phys_to_coord_ratio
  194. ## cx2=phys_width*phys_to_coord_ratio
  195. ## cy2=0.0
  196. # elif coord_type == 'norm':
  197. # if origin == 'center':
  198. # cx1=-1.0
  199. # cy1=1.0
  200. # cx2=1.0
  201. # cy2=-1.0
  202. ## elif origin == 'top_left':
  203. ## cx1=0.0
  204. ## cy1=0.0
  205. ## cx2=1.0
  206. ## cy2=1.0
  207. ## elif origin == 'bottom_left':
  208. ## cx1=0.0
  209. ## cy1=1.0
  210. ## cx2=1.0
  211. ## cy2=0.0
  212. ## elif coord_type == 'percent':
  213. ## if origin == 'center':
  214. ## cx1=-50.0
  215. ## cy1=50.0
  216. ## cx2=50.0
  217. ## cy2=-50.0
  218. ## elif origin == 'top_left':
  219. ## cx1=0.0
  220. ## cy1=0.0
  221. ## cx2=100.0
  222. ## cy2=100.0
  223. ## elif origin == 'bottom_left':
  224. ## cx1=0.0
  225. ## cy1=100.0
  226. ## cx2=100.0
  227. ## cy2=0.0
  228. # elif coord_type == 'deg':
  229. # if origin == 'center':
  230. # cx1=-degree_width/2.0
  231. # cy1=degree_height/2.0
  232. # cx2=degree_width/2.0
  233. # cy2=-degree_height/2.0
  234. ## elif origin == 'top_left':
  235. ## cx1=0.0
  236. ## cy1=0.0
  237. ## cx2=degree_width
  238. ## cy2=degree_height
  239. ## elif origin == 'bottom_left':
  240. ## cx1=0.0
  241. ## cy1=degree_height
  242. ## cx2=degree_width
  243. ## cy2=0.0
  244. #
  245. # if cx1 is not None and cy1 is not None and cx2 is not None and cy2 is not None :
  246. # coord_matrix=np.matrix( [[cx1],[cy1],[cx2],[cy2]] )
  247. # abcd = np.linalg.solve(bounds_matrix, coord_matrix)
  248. # a,b,c,d=np.array(abcd)[:,0]
  249. # #print2err('abcd: {0}\n a={1}, b={2} , c={3}, d={4}'.format(abcd,a,b,c,d))
  250. #
  251. #
  252. # def pix2coord(self, x,y,display_index=None):
  253. # #print2err('Display {0} bounds: {1}'.format(display_index,self.getBounds()))
  254. # if display_index == self.getIndex():
  255. # return a*x+b*y+c, b*x-a*y+d
  256. # return x,y
  257. #
  258. # self._pix2coord=pix2coord
  259. #
  260. # def coord2pix(self,cx,cy,display_index=None):
  261. # if display_index == self.getIndex():
  262. # aabb=(a**2+b**2)
  263. # return (a*cx+b*cy-b*d-a*c)/aabb, (b*cx-a*cy-b*c+a*d)/aabb
  264. # return cx,cy
  265. #
  266. # self._coord2pix=coord2pix