psychopy 2025.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3127) hide show
  1. psychopy/CHANGELOG.txt +3981 -0
  2. psychopy/GIT_SHA +1 -0
  3. psychopy/LICENSE.txt +11 -0
  4. psychopy/LICENSES.txt +55 -0
  5. psychopy/VERSION +1 -0
  6. psychopy/__init__.py +148 -0
  7. psychopy/alerts/__init__.py +65 -0
  8. psychopy/alerts/_alerts.py +224 -0
  9. psychopy/alerts/_errorHandler.py +50 -0
  10. psychopy/alerts/alertsCatalogue/1000.yaml +0 -0
  11. psychopy/alerts/alertsCatalogue/1100.yaml +0 -0
  12. psychopy/alerts/alertsCatalogue/1500.yaml +0 -0
  13. psychopy/alerts/alertsCatalogue/2000.yaml +0 -0
  14. psychopy/alerts/alertsCatalogue/2100.yaml +0 -0
  15. psychopy/alerts/alertsCatalogue/2110.yaml +0 -0
  16. psychopy/alerts/alertsCatalogue/2115.yaml +21 -0
  17. psychopy/alerts/alertsCatalogue/2120.yaml +22 -0
  18. psychopy/alerts/alertsCatalogue/2150.yaml +0 -0
  19. psychopy/alerts/alertsCatalogue/2155.yaml +21 -0
  20. psychopy/alerts/alertsCatalogue/2300.yaml +0 -0
  21. psychopy/alerts/alertsCatalogue/2500.yaml +0 -0
  22. psychopy/alerts/alertsCatalogue/3000.yaml +0 -0
  23. psychopy/alerts/alertsCatalogue/3100.yaml +0 -0
  24. psychopy/alerts/alertsCatalogue/3110.yaml +25 -0
  25. psychopy/alerts/alertsCatalogue/3115.yaml +24 -0
  26. psychopy/alerts/alertsCatalogue/3200.yaml +0 -0
  27. psychopy/alerts/alertsCatalogue/3210.yaml +27 -0
  28. psychopy/alerts/alertsCatalogue/3400.yaml +0 -0
  29. psychopy/alerts/alertsCatalogue/3600.yaml +0 -0
  30. psychopy/alerts/alertsCatalogue/3610.yaml +19 -0
  31. psychopy/alerts/alertsCatalogue/4000.yaml +0 -0
  32. psychopy/alerts/alertsCatalogue/4051.yaml +19 -0
  33. psychopy/alerts/alertsCatalogue/4052.yaml +19 -0
  34. psychopy/alerts/alertsCatalogue/4100.yaml +0 -0
  35. psychopy/alerts/alertsCatalogue/4105.yaml +18 -0
  36. psychopy/alerts/alertsCatalogue/4115.yaml +21 -0
  37. psychopy/alerts/alertsCatalogue/4120.yaml +19 -0
  38. psychopy/alerts/alertsCatalogue/4125.yaml +23 -0
  39. psychopy/alerts/alertsCatalogue/4130.yaml +19 -0
  40. psychopy/alerts/alertsCatalogue/4160.yaml +25 -0
  41. psychopy/alerts/alertsCatalogue/4200.yaml +0 -0
  42. psychopy/alerts/alertsCatalogue/4205.yaml +19 -0
  43. psychopy/alerts/alertsCatalogue/4210.yaml +19 -0
  44. psychopy/alerts/alertsCatalogue/4305.yaml +21 -0
  45. psychopy/alerts/alertsCatalogue/4310.yaml +21 -0
  46. psychopy/alerts/alertsCatalogue/4315.yaml +21 -0
  47. psychopy/alerts/alertsCatalogue/4320.yaml +19 -0
  48. psychopy/alerts/alertsCatalogue/4325.yaml +23 -0
  49. psychopy/alerts/alertsCatalogue/4330.yaml +19 -0
  50. psychopy/alerts/alertsCatalogue/4335.yaml +19 -0
  51. psychopy/alerts/alertsCatalogue/4340.yaml +19 -0
  52. psychopy/alerts/alertsCatalogue/4405.yaml +24 -0
  53. psychopy/alerts/alertsCatalogue/4505.yaml +22 -0
  54. psychopy/alerts/alertsCatalogue/4510.yaml +25 -0
  55. psychopy/alerts/alertsCatalogue/4520.yaml +26 -0
  56. psychopy/alerts/alertsCatalogue/4530.yaml +23 -0
  57. psychopy/alerts/alertsCatalogue/4540.yaml +19 -0
  58. psychopy/alerts/alertsCatalogue/4545.yaml +22 -0
  59. psychopy/alerts/alertsCatalogue/4550.yaml +19 -0
  60. psychopy/alerts/alertsCatalogue/4605.yaml +22 -0
  61. psychopy/alerts/alertsCatalogue/4610.yaml +22 -0
  62. psychopy/alerts/alertsCatalogue/4615.yaml +19 -0
  63. psychopy/alerts/alertsCatalogue/4705.yaml +19 -0
  64. psychopy/alerts/alertsCatalogue/4710.yaml +19 -0
  65. psychopy/alerts/alertsCatalogue/4810.yaml +19 -0
  66. psychopy/alerts/alertsCatalogue/5000.yaml +0 -0
  67. psychopy/alerts/alertsCatalogue/5055.yaml +25 -0
  68. psychopy/alerts/alertsCatalogue/6000.yaml +0 -0
  69. psychopy/alerts/alertsCatalogue/6105.yaml +18 -0
  70. psychopy/alerts/alertsCatalogue/7105.yaml +19 -0
  71. psychopy/alerts/alertsCatalogue/8105.yaml +21 -0
  72. psychopy/alerts/alertsCatalogue/8110.yaml +21 -0
  73. psychopy/alerts/alertsCatalogue/9998.yaml +19 -0
  74. psychopy/alerts/alertsCatalogue/9999.yaml +19 -0
  75. psychopy/alerts/alertsCatalogue/alertCategories.yaml +123 -0
  76. psychopy/alerts/alertsCatalogue/alertTemplate.yaml +19 -0
  77. psychopy/alerts/alertsCatalogue/alertmsg.py +173 -0
  78. psychopy/alerts/alertsCatalogue/generateAlertmsg.py +34 -0
  79. psychopy/alerts/alerttools.py +322 -0
  80. psychopy/app/Resources/README.md +12 -0
  81. psychopy/app/Resources/__init__.py +0 -0
  82. psychopy/app/Resources/betasplash.png +0 -0
  83. psychopy/app/Resources/betasplash@2x.png +0 -0
  84. psychopy/app/Resources/builder.ico +0 -0
  85. psychopy/app/Resources/classic/FlowBottom_CompLeft.png +0 -0
  86. psychopy/app/Resources/classic/FlowBottom_CompRight.png +0 -0
  87. psychopy/app/Resources/classic/FlowTop_CompLeft.png +0 -0
  88. psychopy/app/Resources/classic/FlowTop_CompRight.png +0 -0
  89. psychopy/app/Resources/classic/README.txt +9 -0
  90. psychopy/app/Resources/classic/__init__.py +0 -0
  91. psychopy/app/Resources/classic/_layouts.ai +1138 -1
  92. psychopy/app/Resources/classic/add.png +0 -0
  93. psychopy/app/Resources/classic/add@2x.png +0 -0
  94. psychopy/app/Resources/classic/addExp32.png +0 -0
  95. psychopy/app/Resources/classic/add_many.png +0 -0
  96. psychopy/app/Resources/classic/add_many@2x.png +0 -0
  97. psychopy/app/Resources/classic/alerts.png +0 -0
  98. psychopy/app/Resources/classic/beta.png +0 -0
  99. psychopy/app/Resources/classic/browser.png +0 -0
  100. psychopy/app/Resources/classic/browser@2x.png +0 -0
  101. psychopy/app/Resources/classic/bug16.png +0 -0
  102. psychopy/app/Resources/classic/case.png +0 -0
  103. psychopy/app/Resources/classic/case@2x.png +0 -0
  104. psychopy/app/Resources/classic/circle_mask.png +0 -0
  105. psychopy/app/Resources/classic/circle_mask@2x.png +0 -0
  106. psychopy/app/Resources/classic/clear.png +0 -0
  107. psychopy/app/Resources/classic/clear@2x.png +0 -0
  108. psychopy/app/Resources/classic/coderclass16.png +0 -0
  109. psychopy/app/Resources/classic/coderfunc16.png +0 -0
  110. psychopy/app/Resources/classic/coderimport16.png +0 -0
  111. psychopy/app/Resources/classic/coderjs16.png +0 -0
  112. psychopy/app/Resources/classic/coderpython16.png +0 -0
  113. psychopy/app/Resources/classic/codervar16.png +0 -0
  114. psychopy/app/Resources/classic/cogwindow32.png +0 -0
  115. psychopy/app/Resources/classic/color32.png +0 -0
  116. psychopy/app/Resources/classic/compile32.png +0 -0
  117. psychopy/app/Resources/classic/compile_js.png +0 -0
  118. psychopy/app/Resources/classic/compile_js@2x.png +0 -0
  119. psychopy/app/Resources/classic/compile_py.png +0 -0
  120. psychopy/app/Resources/classic/compile_py@2x.png +0 -0
  121. psychopy/app/Resources/classic/copy16.png +0 -0
  122. psychopy/app/Resources/classic/currentFile.png +0 -0
  123. psychopy/app/Resources/classic/currentFile@2x.png +0 -0
  124. psychopy/app/Resources/classic/delete16.png +0 -0
  125. psychopy/app/Resources/classic/desktop.png +0 -0
  126. psychopy/app/Resources/classic/desktop@2x.png +0 -0
  127. psychopy/app/Resources/classic/devices.png +0 -0
  128. psychopy/app/Resources/classic/devices@2x.png +0 -0
  129. psychopy/app/Resources/classic/dirup16.png +0 -0
  130. psychopy/app/Resources/classic/docclose16.png +0 -0
  131. psychopy/app/Resources/classic/download.png +0 -0
  132. psychopy/app/Resources/classic/edit.png +0 -0
  133. psychopy/app/Resources/classic/edit@2x.png +0 -0
  134. psychopy/app/Resources/classic/editbtn.png +0 -0
  135. psychopy/app/Resources/classic/editbtn16.png +0 -0
  136. psychopy/app/Resources/classic/editbtn16@2x.png +0 -0
  137. psychopy/app/Resources/classic/editbtn@2x.png +0 -0
  138. psychopy/app/Resources/classic/email.png +0 -0
  139. psychopy/app/Resources/classic/email@2x.png +0 -0
  140. psychopy/app/Resources/classic/experiment.png +0 -0
  141. psychopy/app/Resources/classic/experiment@2x.png +0 -0
  142. psychopy/app/Resources/classic/expsettings.png +0 -0
  143. psychopy/app/Resources/classic/expsettings@2x.png +0 -0
  144. psychopy/app/Resources/classic/file.png +0 -0
  145. psychopy/app/Resources/classic/file@2x.png +0 -0
  146. psychopy/app/Resources/classic/fileaudio.png +0 -0
  147. psychopy/app/Resources/classic/fileaudio@2x.png +0 -0
  148. psychopy/app/Resources/classic/filecss.png +0 -0
  149. psychopy/app/Resources/classic/filecss@2x.png +0 -0
  150. psychopy/app/Resources/classic/filecsv.png +0 -0
  151. psychopy/app/Resources/classic/filecsv@2x.png +0 -0
  152. psychopy/app/Resources/classic/filedesign.png +0 -0
  153. psychopy/app/Resources/classic/filedesign@2x.png +0 -0
  154. psychopy/app/Resources/classic/filefont.png +0 -0
  155. psychopy/app/Resources/classic/filefont@2x.png +0 -0
  156. psychopy/app/Resources/classic/filegit.png +0 -0
  157. psychopy/app/Resources/classic/filegit@2x.png +0 -0
  158. psychopy/app/Resources/classic/filehtml.png +0 -0
  159. psychopy/app/Resources/classic/filehtml@2x.png +0 -0
  160. psychopy/app/Resources/classic/fileimage.png +0 -0
  161. psychopy/app/Resources/classic/fileimage@2x.png +0 -0
  162. psychopy/app/Resources/classic/fileinfo.png +0 -0
  163. psychopy/app/Resources/classic/fileinfo@2x.png +0 -0
  164. psychopy/app/Resources/classic/filejs.png +0 -0
  165. psychopy/app/Resources/classic/filejs@2x.png +0 -0
  166. psychopy/app/Resources/classic/filejson.png +0 -0
  167. psychopy/app/Resources/classic/filejson@2x.png +0 -0
  168. psychopy/app/Resources/classic/filenew.png +0 -0
  169. psychopy/app/Resources/classic/filenew32.png +0 -0
  170. psychopy/app/Resources/classic/filenew@2x.png +0 -0
  171. psychopy/app/Resources/classic/fileopen.png +0 -0
  172. psychopy/app/Resources/classic/fileopen32.png +0 -0
  173. psychopy/app/Resources/classic/fileopen@2x.png +0 -0
  174. psychopy/app/Resources/classic/filepkg.png +0 -0
  175. psychopy/app/Resources/classic/filepkg@2x.png +0 -0
  176. psychopy/app/Resources/classic/filepsyexp.png +0 -0
  177. psychopy/app/Resources/classic/filepsyexp@2x.png +0 -0
  178. psychopy/app/Resources/classic/filepy.png +0 -0
  179. psychopy/app/Resources/classic/filepy@2x.png +0 -0
  180. psychopy/app/Resources/classic/filesave.png +0 -0
  181. psychopy/app/Resources/classic/filesave32.png +0 -0
  182. psychopy/app/Resources/classic/filesave@2x.png +0 -0
  183. psychopy/app/Resources/classic/filesaveas.png +0 -0
  184. psychopy/app/Resources/classic/filesaveas32.png +0 -0
  185. psychopy/app/Resources/classic/filesaveas@2x.png +0 -0
  186. psychopy/app/Resources/classic/filetxt.png +0 -0
  187. psychopy/app/Resources/classic/filetxt@2x.png +0 -0
  188. psychopy/app/Resources/classic/fileunknown.png +0 -0
  189. psychopy/app/Resources/classic/fileunknown@2x.png +0 -0
  190. psychopy/app/Resources/classic/filevideo.png +0 -0
  191. psychopy/app/Resources/classic/filevideo@2x.png +0 -0
  192. psychopy/app/Resources/classic/filter.png +0 -0
  193. psychopy/app/Resources/classic/filter@2x.png +0 -0
  194. psychopy/app/Resources/classic/find.png +0 -0
  195. psychopy/app/Resources/classic/find@2x.png +0 -0
  196. psychopy/app/Resources/classic/folder-open16.png +0 -0
  197. psychopy/app/Resources/classic/folder16.png +0 -0
  198. psychopy/app/Resources/classic/foldernew16.png +0 -0
  199. psychopy/app/Resources/classic/fork.png +0 -0
  200. psychopy/app/Resources/classic/fork@2x.png +0 -0
  201. psychopy/app/Resources/classic/github.png +0 -0
  202. psychopy/app/Resources/classic/github@2x.png +0 -0
  203. psychopy/app/Resources/classic/globe.png +0 -0
  204. psychopy/app/Resources/classic/globe@2x.png +0 -0
  205. psychopy/app/Resources/classic/globe_bug.png +0 -0
  206. psychopy/app/Resources/classic/globe_bug@2x.png +0 -0
  207. psychopy/app/Resources/classic/globe_greensync.png +0 -0
  208. psychopy/app/Resources/classic/globe_greensync@2x.png +0 -0
  209. psychopy/app/Resources/classic/globe_info.png +0 -0
  210. psychopy/app/Resources/classic/globe_info@2x.png +0 -0
  211. psychopy/app/Resources/classic/globe_magnifier.png +0 -0
  212. psychopy/app/Resources/classic/globe_magnifier@2x.png +0 -0
  213. psychopy/app/Resources/classic/globe_run.png +0 -0
  214. psychopy/app/Resources/classic/globe_run@2x.png +0 -0
  215. psychopy/app/Resources/classic/globe_user.png +0 -0
  216. psychopy/app/Resources/classic/globe_user@2x.png +0 -0
  217. psychopy/app/Resources/classic/goto16.png +0 -0
  218. psychopy/app/Resources/classic/greendot.png +0 -0
  219. psychopy/app/Resources/classic/greendot@2x.png +0 -0
  220. psychopy/app/Resources/classic/greenglobe.png +0 -0
  221. psychopy/app/Resources/classic/greenglobe@2x.png +0 -0
  222. psychopy/app/Resources/classic/greenglobe_bug.png +0 -0
  223. psychopy/app/Resources/classic/greenglobe_bug@2x.png +0 -0
  224. psychopy/app/Resources/classic/greenglobe_greensync.png +0 -0
  225. psychopy/app/Resources/classic/greenglobe_greensync@2x.png +0 -0
  226. psychopy/app/Resources/classic/greenglobe_info.png +0 -0
  227. psychopy/app/Resources/classic/greenglobe_info@2x.png +0 -0
  228. psychopy/app/Resources/classic/greenglobe_magnifier.png +0 -0
  229. psychopy/app/Resources/classic/greenglobe_magnifier@2x.png +0 -0
  230. psychopy/app/Resources/classic/greenglobe_run.png +0 -0
  231. psychopy/app/Resources/classic/greenglobe_run@2x.png +0 -0
  232. psychopy/app/Resources/classic/greenglobe_user.png +0 -0
  233. psychopy/app/Resources/classic/greenglobe_user@2x.png +0 -0
  234. psychopy/app/Resources/classic/greydot.png +0 -0
  235. psychopy/app/Resources/classic/greydot@2x.png +0 -0
  236. psychopy/app/Resources/classic/greytick.png +0 -0
  237. psychopy/app/Resources/classic/greytick@2x.png +0 -0
  238. psychopy/app/Resources/classic/invalid_img.png +0 -0
  239. psychopy/app/Resources/classic/jsPilot.png +0 -0
  240. psychopy/app/Resources/classic/jsPilot@2x.png +0 -0
  241. psychopy/app/Resources/classic/jsRun.png +0 -0
  242. psychopy/app/Resources/classic/jsRun@2x.png +0 -0
  243. psychopy/app/Resources/classic/libroot.png +0 -0
  244. psychopy/app/Resources/classic/libroot@2x.png +0 -0
  245. psychopy/app/Resources/classic/loop.png +0 -0
  246. psychopy/app/Resources/classic/loop@2x.png +0 -0
  247. psychopy/app/Resources/classic/monitor16.png +0 -0
  248. psychopy/app/Resources/classic/monitors.png +0 -0
  249. psychopy/app/Resources/classic/monitors16.png +0 -0
  250. psychopy/app/Resources/classic/monitors32.png +0 -0
  251. psychopy/app/Resources/classic/monitors@2x.png +0 -0
  252. psychopy/app/Resources/classic/orangedot.png +0 -0
  253. psychopy/app/Resources/classic/orangedot@2x.png +0 -0
  254. psychopy/app/Resources/classic/pavlovia.png +0 -0
  255. psychopy/app/Resources/classic/pavlovia16.png +0 -0
  256. psychopy/app/Resources/classic/pavlovia16@2x.png +0 -0
  257. psychopy/app/Resources/classic/pavlovia@2x.png +0 -0
  258. psychopy/app/Resources/classic/pavsync.png +0 -0
  259. psychopy/app/Resources/classic/pavsync@2x.png +0 -0
  260. psychopy/app/Resources/classic/person_off.png +0 -0
  261. psychopy/app/Resources/classic/person_off@2x.png +0 -0
  262. psychopy/app/Resources/classic/person_on.png +0 -0
  263. psychopy/app/Resources/classic/person_on@2x.png +0 -0
  264. psychopy/app/Resources/classic/photometer.png +0 -0
  265. psychopy/app/Resources/classic/photometer@2x.png +0 -0
  266. psychopy/app/Resources/classic/plugin16.png +0 -0
  267. psychopy/app/Resources/classic/plugin16@2x.png +0 -0
  268. psychopy/app/Resources/classic/plugins32.png +0 -0
  269. psychopy/app/Resources/classic/plus.png +0 -0
  270. psychopy/app/Resources/classic/plus@2x.png +0 -0
  271. psychopy/app/Resources/classic/preferences-app.png +0 -0
  272. psychopy/app/Resources/classic/preferences-app48.png +0 -0
  273. psychopy/app/Resources/classic/preferences-app@2x.png +0 -0
  274. psychopy/app/Resources/classic/preferences-conn.png +0 -0
  275. psychopy/app/Resources/classic/preferences-conn48.png +0 -0
  276. psychopy/app/Resources/classic/preferences-conn@2x.png +0 -0
  277. psychopy/app/Resources/classic/preferences-debug.png +0 -0
  278. psychopy/app/Resources/classic/preferences-debug@2x.png +0 -0
  279. psychopy/app/Resources/classic/preferences-general.png +0 -0
  280. psychopy/app/Resources/classic/preferences-general48.png +0 -0
  281. psychopy/app/Resources/classic/preferences-general@2x.png +0 -0
  282. psychopy/app/Resources/classic/preferences-hardware.png +0 -0
  283. psychopy/app/Resources/classic/preferences-hardware48.png +0 -0
  284. psychopy/app/Resources/classic/preferences-hardware@2x.png +0 -0
  285. psychopy/app/Resources/classic/preferences-keyboard.png +0 -0
  286. psychopy/app/Resources/classic/preferences-keyboard48.png +0 -0
  287. psychopy/app/Resources/classic/preferences-keyboard@2x.png +0 -0
  288. psychopy/app/Resources/classic/preferences-pilot.png +0 -0
  289. psychopy/app/Resources/classic/preferences-pilot@2x.png +0 -0
  290. psychopy/app/Resources/classic/preferences32.png +0 -0
  291. psychopy/app/Resources/classic/pyPilot.png +0 -0
  292. psychopy/app/Resources/classic/pyPilot@2x.png +0 -0
  293. psychopy/app/Resources/classic/pyRun.png +0 -0
  294. psychopy/app/Resources/classic/pyRun@2x.png +0 -0
  295. psychopy/app/Resources/classic/reddot.png +0 -0
  296. psychopy/app/Resources/classic/reddot@2x.png +0 -0
  297. psychopy/app/Resources/classic/redglobe.png +0 -0
  298. psychopy/app/Resources/classic/redglobe@2x.png +0 -0
  299. psychopy/app/Resources/classic/redglobe_bug.png +0 -0
  300. psychopy/app/Resources/classic/redglobe_bug@2x.png +0 -0
  301. psychopy/app/Resources/classic/redglobe_greensync.png +0 -0
  302. psychopy/app/Resources/classic/redglobe_greensync@2x.png +0 -0
  303. psychopy/app/Resources/classic/redglobe_info.png +0 -0
  304. psychopy/app/Resources/classic/redglobe_info@2x.png +0 -0
  305. psychopy/app/Resources/classic/redglobe_magnifier.png +0 -0
  306. psychopy/app/Resources/classic/redglobe_magnifier@2x.png +0 -0
  307. psychopy/app/Resources/classic/redglobe_run.png +0 -0
  308. psychopy/app/Resources/classic/redglobe_run@2x.png +0 -0
  309. psychopy/app/Resources/classic/redglobe_user.png +0 -0
  310. psychopy/app/Resources/classic/redglobe_user@2x.png +0 -0
  311. psychopy/app/Resources/classic/redo.png +0 -0
  312. psychopy/app/Resources/classic/redo32.png +0 -0
  313. psychopy/app/Resources/classic/redo@2x.png +0 -0
  314. psychopy/app/Resources/classic/regex.png +0 -0
  315. psychopy/app/Resources/classic/regex@2x.png +0 -0
  316. psychopy/app/Resources/classic/removeExp32.png +0 -0
  317. psychopy/app/Resources/classic/rename16.png +0 -0
  318. psychopy/app/Resources/classic/restart.png +0 -0
  319. psychopy/app/Resources/classic/restart@2x.png +0 -0
  320. psychopy/app/Resources/classic/run.png +0 -0
  321. psychopy/app/Resources/classic/run@2x.png +0 -0
  322. psychopy/app/Resources/classic/runner.png +0 -0
  323. psychopy/app/Resources/classic/runner@2x.png +0 -0
  324. psychopy/app/Resources/classic/runnerPilot.png +0 -0
  325. psychopy/app/Resources/classic/runnerPilot@2x.png +0 -0
  326. psychopy/app/Resources/classic/savebtn.png +0 -0
  327. psychopy/app/Resources/classic/savebtn16.png +0 -0
  328. psychopy/app/Resources/classic/savebtn16@2x.png +0 -0
  329. psychopy/app/Resources/classic/savebtn@2x.png +0 -0
  330. psychopy/app/Resources/classic/search.png +0 -0
  331. psychopy/app/Resources/classic/search@2x.png +0 -0
  332. psychopy/app/Resources/classic/showBuilder.png +0 -0
  333. psychopy/app/Resources/classic/showBuilder@2x.png +0 -0
  334. psychopy/app/Resources/classic/showCoder.png +0 -0
  335. psychopy/app/Resources/classic/showCoder@2x.png +0 -0
  336. psychopy/app/Resources/classic/showRunner.png +0 -0
  337. psychopy/app/Resources/classic/showRunner@2x.png +0 -0
  338. psychopy/app/Resources/classic/starred.png +0 -0
  339. psychopy/app/Resources/classic/starred@2x.png +0 -0
  340. psychopy/app/Resources/classic/start.png +0 -0
  341. psychopy/app/Resources/classic/start@2x.png +0 -0
  342. psychopy/app/Resources/classic/stdout.png +0 -0
  343. psychopy/app/Resources/classic/stop.png +0 -0
  344. psychopy/app/Resources/classic/stop32.png +0 -0
  345. psychopy/app/Resources/classic/stop@2x.png +0 -0
  346. psychopy/app/Resources/classic/switchCtrlBot.png +0 -0
  347. psychopy/app/Resources/classic/switchCtrlBot@2x.png +0 -0
  348. psychopy/app/Resources/classic/switchCtrlLeft.png +0 -0
  349. psychopy/app/Resources/classic/switchCtrlLeft@2x.png +0 -0
  350. psychopy/app/Resources/classic/switchCtrlRight.png +0 -0
  351. psychopy/app/Resources/classic/switchCtrlRight@2x.png +0 -0
  352. psychopy/app/Resources/classic/switchCtrlTop.png +0 -0
  353. psychopy/app/Resources/classic/switchCtrlTop@2x.png +0 -0
  354. psychopy/app/Resources/classic/tick.png +0 -0
  355. psychopy/app/Resources/classic/tick@2x.png +0 -0
  356. psychopy/app/Resources/classic/undo.png +0 -0
  357. psychopy/app/Resources/classic/undo32.png +0 -0
  358. psychopy/app/Resources/classic/undo@2x.png +0 -0
  359. psychopy/app/Resources/classic/unstarred.png +0 -0
  360. psychopy/app/Resources/classic/unstarred@2x.png +0 -0
  361. psychopy/app/Resources/classic/user_none.png +0 -0
  362. psychopy/app/Resources/classic/view-refresh16.png +0 -0
  363. psychopy/app/Resources/classic/viewbtn.png +0 -0
  364. psychopy/app/Resources/classic/viewbtn16.png +0 -0
  365. psychopy/app/Resources/classic/viewbtn16@2x.png +0 -0
  366. psychopy/app/Resources/classic/viewbtn@2x.png +0 -0
  367. psychopy/app/Resources/classic/windows16.png +0 -0
  368. psychopy/app/Resources/classic/windows16@2x.png +0 -0
  369. psychopy/app/Resources/coder.ico +0 -0
  370. psychopy/app/Resources/dark/FlowBottom_CompLeft.png +0 -0
  371. psychopy/app/Resources/dark/FlowBottom_CompRight.png +0 -0
  372. psychopy/app/Resources/dark/FlowTop_CompLeft.png +0 -0
  373. psychopy/app/Resources/dark/FlowTop_CompRight.png +0 -0
  374. psychopy/app/Resources/dark/README.txt +3 -0
  375. psychopy/app/Resources/dark/__init__.py +0 -0
  376. psychopy/app/Resources/dark/add.png +0 -0
  377. psychopy/app/Resources/dark/add@2x.png +0 -0
  378. psychopy/app/Resources/dark/addExp32.png +0 -0
  379. psychopy/app/Resources/dark/addExp32@2x.png +0 -0
  380. psychopy/app/Resources/dark/add_many.png +0 -0
  381. psychopy/app/Resources/dark/add_many@2x.png +0 -0
  382. psychopy/app/Resources/dark/alerts.png +0 -0
  383. psychopy/app/Resources/dark/alerts@2x.png +0 -0
  384. psychopy/app/Resources/dark/beta.png +0 -0
  385. psychopy/app/Resources/dark/beta@2x.png +0 -0
  386. psychopy/app/Resources/dark/browser.png +0 -0
  387. psychopy/app/Resources/dark/browser@2x.png +0 -0
  388. psychopy/app/Resources/dark/bug16.png +0 -0
  389. psychopy/app/Resources/dark/bug16@2x.png +0 -0
  390. psychopy/app/Resources/dark/case.png +0 -0
  391. psychopy/app/Resources/dark/case@2x.png +0 -0
  392. psychopy/app/Resources/dark/circle_mask.png +0 -0
  393. psychopy/app/Resources/dark/circle_mask@2x.png +0 -0
  394. psychopy/app/Resources/dark/clear.png +0 -0
  395. psychopy/app/Resources/dark/clear@2x.png +0 -0
  396. psychopy/app/Resources/dark/coderclass16.png +0 -0
  397. psychopy/app/Resources/dark/coderclass16@2x.png +0 -0
  398. psychopy/app/Resources/dark/coderfunc16.png +0 -0
  399. psychopy/app/Resources/dark/coderfunc16@2x.png +0 -0
  400. psychopy/app/Resources/dark/coderimport16.png +0 -0
  401. psychopy/app/Resources/dark/coderimport16@2x.png +0 -0
  402. psychopy/app/Resources/dark/coderjs.png +0 -0
  403. psychopy/app/Resources/dark/coderjs@2x.png +0 -0
  404. psychopy/app/Resources/dark/coderpython.png +0 -0
  405. psychopy/app/Resources/dark/coderpython@2x.png +0 -0
  406. psychopy/app/Resources/dark/codervar16.png +0 -0
  407. psychopy/app/Resources/dark/codervar16@2x.png +0 -0
  408. psychopy/app/Resources/dark/cogwindow32.png +0 -0
  409. psychopy/app/Resources/dark/cogwindow32@2x.png +0 -0
  410. psychopy/app/Resources/dark/color32.png +0 -0
  411. psychopy/app/Resources/dark/color32@2x.png +0 -0
  412. psychopy/app/Resources/dark/compile_js.png +0 -0
  413. psychopy/app/Resources/dark/compile_js@2x.png +0 -0
  414. psychopy/app/Resources/dark/compile_py.png +0 -0
  415. psychopy/app/Resources/dark/compile_py@2x.png +0 -0
  416. psychopy/app/Resources/dark/copy16.png +0 -0
  417. psychopy/app/Resources/dark/copy16@2x.png +0 -0
  418. psychopy/app/Resources/dark/currentFile16.png +0 -0
  419. psychopy/app/Resources/dark/currentFile16@2x.png +0 -0
  420. psychopy/app/Resources/dark/delete16.png +0 -0
  421. psychopy/app/Resources/dark/delete16@2x.png +0 -0
  422. psychopy/app/Resources/dark/desktop.png +0 -0
  423. psychopy/app/Resources/dark/desktop@2x.png +0 -0
  424. psychopy/app/Resources/dark/devices.png +0 -0
  425. psychopy/app/Resources/dark/devices@2x.png +0 -0
  426. psychopy/app/Resources/dark/dirup16.png +0 -0
  427. psychopy/app/Resources/dark/dirup16@2x.png +0 -0
  428. psychopy/app/Resources/dark/docclose16.png +0 -0
  429. psychopy/app/Resources/dark/docclose16@2x.png +0 -0
  430. psychopy/app/Resources/dark/download.png +0 -0
  431. psychopy/app/Resources/dark/download@2x.png +0 -0
  432. psychopy/app/Resources/dark/edit.png +0 -0
  433. psychopy/app/Resources/dark/edit@2x.png +0 -0
  434. psychopy/app/Resources/dark/editbtn16.png +0 -0
  435. psychopy/app/Resources/dark/editbtn16@2x.png +0 -0
  436. psychopy/app/Resources/dark/email.png +0 -0
  437. psychopy/app/Resources/dark/email@2x.png +0 -0
  438. psychopy/app/Resources/dark/experiment.png +0 -0
  439. psychopy/app/Resources/dark/experiment@2x.png +0 -0
  440. psychopy/app/Resources/dark/expsettings.png +0 -0
  441. psychopy/app/Resources/dark/expsettings@2x.png +0 -0
  442. psychopy/app/Resources/dark/file.png +0 -0
  443. psychopy/app/Resources/dark/file@16w.png +0 -0
  444. psychopy/app/Resources/dark/file@16w@2x.png +0 -0
  445. psychopy/app/Resources/dark/file@2x.png +0 -0
  446. psychopy/app/Resources/dark/fileaudio.png +0 -0
  447. psychopy/app/Resources/dark/fileaudio@2x.png +0 -0
  448. psychopy/app/Resources/dark/filecss.png +0 -0
  449. psychopy/app/Resources/dark/filecss@2x.png +0 -0
  450. psychopy/app/Resources/dark/filecsv.png +0 -0
  451. psychopy/app/Resources/dark/filecsv@2x.png +0 -0
  452. psychopy/app/Resources/dark/filedesign.png +0 -0
  453. psychopy/app/Resources/dark/filedesign@2x.png +0 -0
  454. psychopy/app/Resources/dark/filefont.png +0 -0
  455. psychopy/app/Resources/dark/filefont@2x.png +0 -0
  456. psychopy/app/Resources/dark/filegit.png +0 -0
  457. psychopy/app/Resources/dark/filegit@2x.png +0 -0
  458. psychopy/app/Resources/dark/filehtml.png +0 -0
  459. psychopy/app/Resources/dark/filehtml@2x.png +0 -0
  460. psychopy/app/Resources/dark/fileimage.png +0 -0
  461. psychopy/app/Resources/dark/fileimage@2x.png +0 -0
  462. psychopy/app/Resources/dark/fileinfo.png +0 -0
  463. psychopy/app/Resources/dark/fileinfo@2x.png +0 -0
  464. psychopy/app/Resources/dark/filejs.png +0 -0
  465. psychopy/app/Resources/dark/filejs@2x.png +0 -0
  466. psychopy/app/Resources/dark/filejson.png +0 -0
  467. psychopy/app/Resources/dark/filejson@2x.png +0 -0
  468. psychopy/app/Resources/dark/filenew.png +0 -0
  469. psychopy/app/Resources/dark/filenew32.png +0 -0
  470. psychopy/app/Resources/dark/filenew32@2x.png +0 -0
  471. psychopy/app/Resources/dark/filenew@2x.png +0 -0
  472. psychopy/app/Resources/dark/fileopen.png +0 -0
  473. psychopy/app/Resources/dark/fileopen32.png +0 -0
  474. psychopy/app/Resources/dark/fileopen32@2x.png +0 -0
  475. psychopy/app/Resources/dark/fileopen@2x.png +0 -0
  476. psychopy/app/Resources/dark/filepkg.png +0 -0
  477. psychopy/app/Resources/dark/filepkg@2x.png +0 -0
  478. psychopy/app/Resources/dark/filepsyexp.png +0 -0
  479. psychopy/app/Resources/dark/filepsyexp@2x.png +0 -0
  480. psychopy/app/Resources/dark/filepy.png +0 -0
  481. psychopy/app/Resources/dark/filepy@2x.png +0 -0
  482. psychopy/app/Resources/dark/filesave.png +0 -0
  483. psychopy/app/Resources/dark/filesave32.png +0 -0
  484. psychopy/app/Resources/dark/filesave32@2x.png +0 -0
  485. psychopy/app/Resources/dark/filesave@2x.png +0 -0
  486. psychopy/app/Resources/dark/filesaveas.png +0 -0
  487. psychopy/app/Resources/dark/filesaveas32.png +0 -0
  488. psychopy/app/Resources/dark/filesaveas32@2x.png +0 -0
  489. psychopy/app/Resources/dark/filesaveas@2x.png +0 -0
  490. psychopy/app/Resources/dark/filetxt.png +0 -0
  491. psychopy/app/Resources/dark/filetxt@2x.png +0 -0
  492. psychopy/app/Resources/dark/fileunknown.png +0 -0
  493. psychopy/app/Resources/dark/fileunknown@2x.png +0 -0
  494. psychopy/app/Resources/dark/filevideo.png +0 -0
  495. psychopy/app/Resources/dark/filevideo@2x.png +0 -0
  496. psychopy/app/Resources/dark/filter.png +0 -0
  497. psychopy/app/Resources/dark/filter@2x.png +0 -0
  498. psychopy/app/Resources/dark/find.png +0 -0
  499. psychopy/app/Resources/dark/find@2x.png +0 -0
  500. psychopy/app/Resources/dark/folder-open16.png +0 -0
  501. psychopy/app/Resources/dark/folder-open16@2x.png +0 -0
  502. psychopy/app/Resources/dark/folder16.png +0 -0
  503. psychopy/app/Resources/dark/folder16@2x.png +0 -0
  504. psychopy/app/Resources/dark/foldernew16.png +0 -0
  505. psychopy/app/Resources/dark/foldernew16@2x.png +0 -0
  506. psychopy/app/Resources/dark/fork.png +0 -0
  507. psychopy/app/Resources/dark/fork@2x.png +0 -0
  508. psychopy/app/Resources/dark/github.png +0 -0
  509. psychopy/app/Resources/dark/github@2x.png +0 -0
  510. psychopy/app/Resources/dark/globe.png +0 -0
  511. psychopy/app/Resources/dark/globe@2x.png +0 -0
  512. psychopy/app/Resources/dark/globe_bug.png +0 -0
  513. psychopy/app/Resources/dark/globe_bug@2x.png +0 -0
  514. psychopy/app/Resources/dark/globe_greensync.png +0 -0
  515. psychopy/app/Resources/dark/globe_greensync@2x.png +0 -0
  516. psychopy/app/Resources/dark/globe_info.png +0 -0
  517. psychopy/app/Resources/dark/globe_info@2x.png +0 -0
  518. psychopy/app/Resources/dark/globe_magnifier.png +0 -0
  519. psychopy/app/Resources/dark/globe_magnifier@2x.png +0 -0
  520. psychopy/app/Resources/dark/globe_run.png +0 -0
  521. psychopy/app/Resources/dark/globe_run@2x.png +0 -0
  522. psychopy/app/Resources/dark/globe_user.png +0 -0
  523. psychopy/app/Resources/dark/globe_user@2x.png +0 -0
  524. psychopy/app/Resources/dark/goto.png +0 -0
  525. psychopy/app/Resources/dark/goto@2x.png +0 -0
  526. psychopy/app/Resources/dark/greendot.png +0 -0
  527. psychopy/app/Resources/dark/greendot@2x.png +0 -0
  528. psychopy/app/Resources/dark/greenglobe.png +0 -0
  529. psychopy/app/Resources/dark/greenglobe@2x.png +0 -0
  530. psychopy/app/Resources/dark/greenglobe_bug.png +0 -0
  531. psychopy/app/Resources/dark/greenglobe_bug@2x.png +0 -0
  532. psychopy/app/Resources/dark/greenglobe_greensync.png +0 -0
  533. psychopy/app/Resources/dark/greenglobe_greensync@2x.png +0 -0
  534. psychopy/app/Resources/dark/greenglobe_info.png +0 -0
  535. psychopy/app/Resources/dark/greenglobe_info@2x.png +0 -0
  536. psychopy/app/Resources/dark/greenglobe_magnifier.png +0 -0
  537. psychopy/app/Resources/dark/greenglobe_magnifier@2x.png +0 -0
  538. psychopy/app/Resources/dark/greenglobe_run.png +0 -0
  539. psychopy/app/Resources/dark/greenglobe_run@2x.png +0 -0
  540. psychopy/app/Resources/dark/greenglobe_user.png +0 -0
  541. psychopy/app/Resources/dark/greenglobe_user@2x.png +0 -0
  542. psychopy/app/Resources/dark/greydot.png +0 -0
  543. psychopy/app/Resources/dark/greydot@2x.png +0 -0
  544. psychopy/app/Resources/dark/greytick.png +0 -0
  545. psychopy/app/Resources/dark/greytick@2x.png +0 -0
  546. psychopy/app/Resources/dark/invalid_img.png +0 -0
  547. psychopy/app/Resources/dark/jsPilot.png +0 -0
  548. psychopy/app/Resources/dark/jsPilot@2x.png +0 -0
  549. psychopy/app/Resources/dark/jsRun.png +0 -0
  550. psychopy/app/Resources/dark/jsRun@2x.png +0 -0
  551. psychopy/app/Resources/dark/libroot16.png +0 -0
  552. psychopy/app/Resources/dark/libroot16@2x.png +0 -0
  553. psychopy/app/Resources/dark/loop.png +0 -0
  554. psychopy/app/Resources/dark/loop@2x.png +0 -0
  555. psychopy/app/Resources/dark/monitor16.png +0 -0
  556. psychopy/app/Resources/dark/monitor16@2x.png +0 -0
  557. psychopy/app/Resources/dark/monitors.png +0 -0
  558. psychopy/app/Resources/dark/monitors32.png +0 -0
  559. psychopy/app/Resources/dark/monitors32@2x.png +0 -0
  560. psychopy/app/Resources/dark/monitors@2x.png +0 -0
  561. psychopy/app/Resources/dark/orangedot.png +0 -0
  562. psychopy/app/Resources/dark/orangedot@2x.png +0 -0
  563. psychopy/app/Resources/dark/pavlovia.png +0 -0
  564. psychopy/app/Resources/dark/pavlovia16.png +0 -0
  565. psychopy/app/Resources/dark/pavlovia16@2x.png +0 -0
  566. psychopy/app/Resources/dark/pavlovia@2x.png +0 -0
  567. psychopy/app/Resources/dark/pavsync.png +0 -0
  568. psychopy/app/Resources/dark/pavsync@2x.png +0 -0
  569. psychopy/app/Resources/dark/person_off.png +0 -0
  570. psychopy/app/Resources/dark/person_off@2x.png +0 -0
  571. psychopy/app/Resources/dark/person_on.png +0 -0
  572. psychopy/app/Resources/dark/person_on@2x.png +0 -0
  573. psychopy/app/Resources/dark/photometer.png +0 -0
  574. psychopy/app/Resources/dark/photometer@2x.png +0 -0
  575. psychopy/app/Resources/dark/plugin16.png +0 -0
  576. psychopy/app/Resources/dark/plugin16@2x.png +0 -0
  577. psychopy/app/Resources/dark/plugins32.png +0 -0
  578. psychopy/app/Resources/dark/plugins32@2x.png +0 -0
  579. psychopy/app/Resources/dark/plus.png +0 -0
  580. psychopy/app/Resources/dark/plus@2x.png +0 -0
  581. psychopy/app/Resources/dark/preferences-app.png +0 -0
  582. psychopy/app/Resources/dark/preferences-app48.png +0 -0
  583. psychopy/app/Resources/dark/preferences-app48@2x.png +0 -0
  584. psychopy/app/Resources/dark/preferences-app@2x.png +0 -0
  585. psychopy/app/Resources/dark/preferences-conn.png +0 -0
  586. psychopy/app/Resources/dark/preferences-conn48.png +0 -0
  587. psychopy/app/Resources/dark/preferences-conn48@2x.png +0 -0
  588. psychopy/app/Resources/dark/preferences-conn@2x.png +0 -0
  589. psychopy/app/Resources/dark/preferences-debug.png +0 -0
  590. psychopy/app/Resources/dark/preferences-debug@2x.png +0 -0
  591. psychopy/app/Resources/dark/preferences-general.png +0 -0
  592. psychopy/app/Resources/dark/preferences-general48.png +0 -0
  593. psychopy/app/Resources/dark/preferences-general48@2x.png +0 -0
  594. psychopy/app/Resources/dark/preferences-general@2x.png +0 -0
  595. psychopy/app/Resources/dark/preferences-hardware.png +0 -0
  596. psychopy/app/Resources/dark/preferences-hardware48.png +0 -0
  597. psychopy/app/Resources/dark/preferences-hardware48@2x.png +0 -0
  598. psychopy/app/Resources/dark/preferences-hardware@2x.png +0 -0
  599. psychopy/app/Resources/dark/preferences-keyboard.png +0 -0
  600. psychopy/app/Resources/dark/preferences-keyboard48.png +0 -0
  601. psychopy/app/Resources/dark/preferences-keyboard48@2x.png +0 -0
  602. psychopy/app/Resources/dark/preferences-keyboard@2x.png +0 -0
  603. psychopy/app/Resources/dark/preferences-pilot.png +0 -0
  604. psychopy/app/Resources/dark/preferences-pilot@2x.png +0 -0
  605. psychopy/app/Resources/dark/preferences32.png +0 -0
  606. psychopy/app/Resources/dark/preferences32@2x.png +0 -0
  607. psychopy/app/Resources/dark/pyPilot.png +0 -0
  608. psychopy/app/Resources/dark/pyPilot@2x.png +0 -0
  609. psychopy/app/Resources/dark/pyRun.png +0 -0
  610. psychopy/app/Resources/dark/pyRun@2x.png +0 -0
  611. psychopy/app/Resources/dark/reddot.png +0 -0
  612. psychopy/app/Resources/dark/reddot@2x.png +0 -0
  613. psychopy/app/Resources/dark/redglobe.png +0 -0
  614. psychopy/app/Resources/dark/redglobe@2x.png +0 -0
  615. psychopy/app/Resources/dark/redglobe_bug.png +0 -0
  616. psychopy/app/Resources/dark/redglobe_bug@2x.png +0 -0
  617. psychopy/app/Resources/dark/redglobe_greensync.png +0 -0
  618. psychopy/app/Resources/dark/redglobe_greensync@2x.png +0 -0
  619. psychopy/app/Resources/dark/redglobe_info.png +0 -0
  620. psychopy/app/Resources/dark/redglobe_info@2x.png +0 -0
  621. psychopy/app/Resources/dark/redglobe_magnifier.png +0 -0
  622. psychopy/app/Resources/dark/redglobe_magnifier@2x.png +0 -0
  623. psychopy/app/Resources/dark/redglobe_run.png +0 -0
  624. psychopy/app/Resources/dark/redglobe_run@2x.png +0 -0
  625. psychopy/app/Resources/dark/redglobe_user.png +0 -0
  626. psychopy/app/Resources/dark/redglobe_user@2x.png +0 -0
  627. psychopy/app/Resources/dark/redo.png +0 -0
  628. psychopy/app/Resources/dark/redo32.png +0 -0
  629. psychopy/app/Resources/dark/redo32@2x.png +0 -0
  630. psychopy/app/Resources/dark/redo@2x.png +0 -0
  631. psychopy/app/Resources/dark/regex.png +0 -0
  632. psychopy/app/Resources/dark/regex@2x.png +0 -0
  633. psychopy/app/Resources/dark/removeExp32.png +0 -0
  634. psychopy/app/Resources/dark/removeExp32@2x.png +0 -0
  635. psychopy/app/Resources/dark/rename16.png +0 -0
  636. psychopy/app/Resources/dark/rename16@2x.png +0 -0
  637. psychopy/app/Resources/dark/restart.png +0 -0
  638. psychopy/app/Resources/dark/restart@2x.png +0 -0
  639. psychopy/app/Resources/dark/runner.png +0 -0
  640. psychopy/app/Resources/dark/runner@2x.png +0 -0
  641. psychopy/app/Resources/dark/runnerPilot.png +0 -0
  642. psychopy/app/Resources/dark/runnerPilot@2x.png +0 -0
  643. psychopy/app/Resources/dark/savebtn16.png +0 -0
  644. psychopy/app/Resources/dark/savebtn16@2x.png +0 -0
  645. psychopy/app/Resources/dark/search.png +0 -0
  646. psychopy/app/Resources/dark/search@2x.png +0 -0
  647. psychopy/app/Resources/dark/showBuilder.png +0 -0
  648. psychopy/app/Resources/dark/showBuilder@2x.png +0 -0
  649. psychopy/app/Resources/dark/showCoder.png +0 -0
  650. psychopy/app/Resources/dark/showCoder@2x.png +0 -0
  651. psychopy/app/Resources/dark/showRunner.png +0 -0
  652. psychopy/app/Resources/dark/showRunner@2x.png +0 -0
  653. psychopy/app/Resources/dark/starred.png +0 -0
  654. psychopy/app/Resources/dark/starred@2x.png +0 -0
  655. psychopy/app/Resources/dark/start.png +0 -0
  656. psychopy/app/Resources/dark/start@2x.png +0 -0
  657. psychopy/app/Resources/dark/stdout.png +0 -0
  658. psychopy/app/Resources/dark/stdout@2x.png +0 -0
  659. psychopy/app/Resources/dark/stop.png +0 -0
  660. psychopy/app/Resources/dark/stop32.png +0 -0
  661. psychopy/app/Resources/dark/stop32@2x.png +0 -0
  662. psychopy/app/Resources/dark/stop@2x.png +0 -0
  663. psychopy/app/Resources/dark/switchCtrlBot.png +0 -0
  664. psychopy/app/Resources/dark/switchCtrlBot@2x.png +0 -0
  665. psychopy/app/Resources/dark/switchCtrlLeft.png +0 -0
  666. psychopy/app/Resources/dark/switchCtrlLeft@2x.png +0 -0
  667. psychopy/app/Resources/dark/switchCtrlRight.png +0 -0
  668. psychopy/app/Resources/dark/switchCtrlRight@2x.png +0 -0
  669. psychopy/app/Resources/dark/switchCtrlTop.png +0 -0
  670. psychopy/app/Resources/dark/switchCtrlTop@2x.png +0 -0
  671. psychopy/app/Resources/dark/tick.png +0 -0
  672. psychopy/app/Resources/dark/tick@2x.png +0 -0
  673. psychopy/app/Resources/dark/undo.png +0 -0
  674. psychopy/app/Resources/dark/undo32.png +0 -0
  675. psychopy/app/Resources/dark/undo32@2x.png +0 -0
  676. psychopy/app/Resources/dark/undo@2x.png +0 -0
  677. psychopy/app/Resources/dark/unstarred.png +0 -0
  678. psychopy/app/Resources/dark/unstarred@2x.png +0 -0
  679. psychopy/app/Resources/dark/user_none.png +0 -0
  680. psychopy/app/Resources/dark/view-refresh16.png +0 -0
  681. psychopy/app/Resources/dark/view-refresh16@2x.png +0 -0
  682. psychopy/app/Resources/dark/viewbtn16.png +0 -0
  683. psychopy/app/Resources/dark/viewbtn16@2x.png +0 -0
  684. psychopy/app/Resources/dark/windows16.png +0 -0
  685. psychopy/app/Resources/dark/windows16@2x.png +0 -0
  686. psychopy/app/Resources/light/FlowBottom_CompLeft.png +0 -0
  687. psychopy/app/Resources/light/FlowBottom_CompRight.png +0 -0
  688. psychopy/app/Resources/light/FlowTop_CompLeft.png +0 -0
  689. psychopy/app/Resources/light/FlowTop_CompRight.png +0 -0
  690. psychopy/app/Resources/light/README.txt +3 -0
  691. psychopy/app/Resources/light/__init__.py +0 -0
  692. psychopy/app/Resources/light/add.png +0 -0
  693. psychopy/app/Resources/light/add@2x.png +0 -0
  694. psychopy/app/Resources/light/addExp32.png +0 -0
  695. psychopy/app/Resources/light/addExp32@2x.png +0 -0
  696. psychopy/app/Resources/light/add_many.png +0 -0
  697. psychopy/app/Resources/light/add_many@2x.png +0 -0
  698. psychopy/app/Resources/light/alerts.png +0 -0
  699. psychopy/app/Resources/light/alerts@2x.png +0 -0
  700. psychopy/app/Resources/light/beta.png +0 -0
  701. psychopy/app/Resources/light/beta@2x.png +0 -0
  702. psychopy/app/Resources/light/browser.png +0 -0
  703. psychopy/app/Resources/light/browser@2x.png +0 -0
  704. psychopy/app/Resources/light/bug16.png +0 -0
  705. psychopy/app/Resources/light/bug16@2x.png +0 -0
  706. psychopy/app/Resources/light/case.png +0 -0
  707. psychopy/app/Resources/light/case@2x.png +0 -0
  708. psychopy/app/Resources/light/circle_mask.png +0 -0
  709. psychopy/app/Resources/light/circle_mask@2x.png +0 -0
  710. psychopy/app/Resources/light/clear.png +0 -0
  711. psychopy/app/Resources/light/clear@2x.png +0 -0
  712. psychopy/app/Resources/light/coderclass16.png +0 -0
  713. psychopy/app/Resources/light/coderclass16@2x.png +0 -0
  714. psychopy/app/Resources/light/coderfunc16.png +0 -0
  715. psychopy/app/Resources/light/coderfunc16@2x.png +0 -0
  716. psychopy/app/Resources/light/coderimport16.png +0 -0
  717. psychopy/app/Resources/light/coderimport16@2x.png +0 -0
  718. psychopy/app/Resources/light/coderjs.png +0 -0
  719. psychopy/app/Resources/light/coderjs@2x.png +0 -0
  720. psychopy/app/Resources/light/coderpython.png +0 -0
  721. psychopy/app/Resources/light/coderpython@2x.png +0 -0
  722. psychopy/app/Resources/light/codervar16.png +0 -0
  723. psychopy/app/Resources/light/codervar16@2x.png +0 -0
  724. psychopy/app/Resources/light/cogwindow32.png +0 -0
  725. psychopy/app/Resources/light/cogwindow32@2x.png +0 -0
  726. psychopy/app/Resources/light/color16.png +0 -0
  727. psychopy/app/Resources/light/color16@2x.png +0 -0
  728. psychopy/app/Resources/light/color32.png +0 -0
  729. psychopy/app/Resources/light/color32@2x.png +0 -0
  730. psychopy/app/Resources/light/compile_js.png +0 -0
  731. psychopy/app/Resources/light/compile_js@2x.png +0 -0
  732. psychopy/app/Resources/light/compile_py.png +0 -0
  733. psychopy/app/Resources/light/compile_py@2x.png +0 -0
  734. psychopy/app/Resources/light/copy16.png +0 -0
  735. psychopy/app/Resources/light/copy16@2x.png +0 -0
  736. psychopy/app/Resources/light/currentFile16.png +0 -0
  737. psychopy/app/Resources/light/currentFile16@2x.png +0 -0
  738. psychopy/app/Resources/light/delete16.png +0 -0
  739. psychopy/app/Resources/light/delete16@2x.png +0 -0
  740. psychopy/app/Resources/light/delete8.png +0 -0
  741. psychopy/app/Resources/light/desktop.png +0 -0
  742. psychopy/app/Resources/light/desktop@2x.png +0 -0
  743. psychopy/app/Resources/light/devices.png +0 -0
  744. psychopy/app/Resources/light/devices@2x.png +0 -0
  745. psychopy/app/Resources/light/dirup16.png +0 -0
  746. psychopy/app/Resources/light/dirup16@2x.png +0 -0
  747. psychopy/app/Resources/light/docclose16.png +0 -0
  748. psychopy/app/Resources/light/docclose16@2x.png +0 -0
  749. psychopy/app/Resources/light/download.png +0 -0
  750. psychopy/app/Resources/light/download@2x.png +0 -0
  751. psychopy/app/Resources/light/edit.png +0 -0
  752. psychopy/app/Resources/light/edit@2x.png +0 -0
  753. psychopy/app/Resources/light/editbtn16.png +0 -0
  754. psychopy/app/Resources/light/editbtn16@2x.png +0 -0
  755. psychopy/app/Resources/light/email.png +0 -0
  756. psychopy/app/Resources/light/email@2x.png +0 -0
  757. psychopy/app/Resources/light/experiment.png +0 -0
  758. psychopy/app/Resources/light/experiment@2x.png +0 -0
  759. psychopy/app/Resources/light/expsettings.png +0 -0
  760. psychopy/app/Resources/light/expsettings@2x.png +0 -0
  761. psychopy/app/Resources/light/file.png +0 -0
  762. psychopy/app/Resources/light/file@2x.png +0 -0
  763. psychopy/app/Resources/light/fileaudio.png +0 -0
  764. psychopy/app/Resources/light/fileaudio@2x.png +0 -0
  765. psychopy/app/Resources/light/filecss.png +0 -0
  766. psychopy/app/Resources/light/filecss@2x.png +0 -0
  767. psychopy/app/Resources/light/filecsv.png +0 -0
  768. psychopy/app/Resources/light/filecsv@2x.png +0 -0
  769. psychopy/app/Resources/light/filedesign.png +0 -0
  770. psychopy/app/Resources/light/filedesign@2x.png +0 -0
  771. psychopy/app/Resources/light/filefont.png +0 -0
  772. psychopy/app/Resources/light/filefont@2x.png +0 -0
  773. psychopy/app/Resources/light/filegit.png +0 -0
  774. psychopy/app/Resources/light/filegit@2x.png +0 -0
  775. psychopy/app/Resources/light/filehtml.png +0 -0
  776. psychopy/app/Resources/light/filehtml@2x.png +0 -0
  777. psychopy/app/Resources/light/fileimage.png +0 -0
  778. psychopy/app/Resources/light/fileimage@2x.png +0 -0
  779. psychopy/app/Resources/light/fileinfo.png +0 -0
  780. psychopy/app/Resources/light/fileinfo@2x.png +0 -0
  781. psychopy/app/Resources/light/filejs.png +0 -0
  782. psychopy/app/Resources/light/filejs@2x.png +0 -0
  783. psychopy/app/Resources/light/filejson.png +0 -0
  784. psychopy/app/Resources/light/filejson@2x.png +0 -0
  785. psychopy/app/Resources/light/filenew.png +0 -0
  786. psychopy/app/Resources/light/filenew32.png +0 -0
  787. psychopy/app/Resources/light/filenew32@2x.png +0 -0
  788. psychopy/app/Resources/light/filenew@2x.png +0 -0
  789. psychopy/app/Resources/light/fileopen.png +0 -0
  790. psychopy/app/Resources/light/fileopen32.png +0 -0
  791. psychopy/app/Resources/light/fileopen32@2x.png +0 -0
  792. psychopy/app/Resources/light/fileopen@2x.png +0 -0
  793. psychopy/app/Resources/light/filepkg.png +0 -0
  794. psychopy/app/Resources/light/filepkg@2x.png +0 -0
  795. psychopy/app/Resources/light/filepsyexp.png +0 -0
  796. psychopy/app/Resources/light/filepsyexp@2x.png +0 -0
  797. psychopy/app/Resources/light/filepy.png +0 -0
  798. psychopy/app/Resources/light/filepy@2x.png +0 -0
  799. psychopy/app/Resources/light/filesave.png +0 -0
  800. psychopy/app/Resources/light/filesave32.png +0 -0
  801. psychopy/app/Resources/light/filesave32@2x.png +0 -0
  802. psychopy/app/Resources/light/filesave@2x.png +0 -0
  803. psychopy/app/Resources/light/filesaveas.png +0 -0
  804. psychopy/app/Resources/light/filesaveas32.png +0 -0
  805. psychopy/app/Resources/light/filesaveas32@2x.png +0 -0
  806. psychopy/app/Resources/light/filesaveas@2x.png +0 -0
  807. psychopy/app/Resources/light/filetxt.png +0 -0
  808. psychopy/app/Resources/light/filetxt@2x.png +0 -0
  809. psychopy/app/Resources/light/fileunknown.png +0 -0
  810. psychopy/app/Resources/light/fileunknown@2x.png +0 -0
  811. psychopy/app/Resources/light/filevideo.png +0 -0
  812. psychopy/app/Resources/light/filevideo@2x.png +0 -0
  813. psychopy/app/Resources/light/filter.png +0 -0
  814. psychopy/app/Resources/light/filter@2x.png +0 -0
  815. psychopy/app/Resources/light/find.png +0 -0
  816. psychopy/app/Resources/light/find@2x.png +0 -0
  817. psychopy/app/Resources/light/folder-open16.png +0 -0
  818. psychopy/app/Resources/light/folder-open16@2x.png +0 -0
  819. psychopy/app/Resources/light/folder16.png +0 -0
  820. psychopy/app/Resources/light/folder16@2x.png +0 -0
  821. psychopy/app/Resources/light/foldernew16.png +0 -0
  822. psychopy/app/Resources/light/foldernew16@2x.png +0 -0
  823. psychopy/app/Resources/light/fork.png +0 -0
  824. psychopy/app/Resources/light/fork@2x.png +0 -0
  825. psychopy/app/Resources/light/github.png +0 -0
  826. psychopy/app/Resources/light/github@2x.png +0 -0
  827. psychopy/app/Resources/light/globe.png +0 -0
  828. psychopy/app/Resources/light/globe@2x.png +0 -0
  829. psychopy/app/Resources/light/globe_bug.png +0 -0
  830. psychopy/app/Resources/light/globe_bug@2x.png +0 -0
  831. psychopy/app/Resources/light/globe_greensync.png +0 -0
  832. psychopy/app/Resources/light/globe_greensync@2x.png +0 -0
  833. psychopy/app/Resources/light/globe_info.png +0 -0
  834. psychopy/app/Resources/light/globe_info@2x.png +0 -0
  835. psychopy/app/Resources/light/globe_magnifier.png +0 -0
  836. psychopy/app/Resources/light/globe_magnifier@2x.png +0 -0
  837. psychopy/app/Resources/light/globe_run.png +0 -0
  838. psychopy/app/Resources/light/globe_run@2x.png +0 -0
  839. psychopy/app/Resources/light/globe_user.png +0 -0
  840. psychopy/app/Resources/light/globe_user@2x.png +0 -0
  841. psychopy/app/Resources/light/goto16.png +0 -0
  842. psychopy/app/Resources/light/goto16@2x.png +0 -0
  843. psychopy/app/Resources/light/greendot.png +0 -0
  844. psychopy/app/Resources/light/greendot@2x.png +0 -0
  845. psychopy/app/Resources/light/greenglobe.png +0 -0
  846. psychopy/app/Resources/light/greenglobe@2x.png +0 -0
  847. psychopy/app/Resources/light/greenglobe_bug.png +0 -0
  848. psychopy/app/Resources/light/greenglobe_bug@2x.png +0 -0
  849. psychopy/app/Resources/light/greenglobe_greensync.png +0 -0
  850. psychopy/app/Resources/light/greenglobe_greensync@2x.png +0 -0
  851. psychopy/app/Resources/light/greenglobe_info.png +0 -0
  852. psychopy/app/Resources/light/greenglobe_info@2x.png +0 -0
  853. psychopy/app/Resources/light/greenglobe_magnifier.png +0 -0
  854. psychopy/app/Resources/light/greenglobe_magnifier@2x.png +0 -0
  855. psychopy/app/Resources/light/greenglobe_run.png +0 -0
  856. psychopy/app/Resources/light/greenglobe_run@2x.png +0 -0
  857. psychopy/app/Resources/light/greenglobe_user.png +0 -0
  858. psychopy/app/Resources/light/greenglobe_user@2x.png +0 -0
  859. psychopy/app/Resources/light/greydot.png +0 -0
  860. psychopy/app/Resources/light/greydot@2x.png +0 -0
  861. psychopy/app/Resources/light/greytick.png +0 -0
  862. psychopy/app/Resources/light/greytick@2x.png +0 -0
  863. psychopy/app/Resources/light/invalid_img.png +0 -0
  864. psychopy/app/Resources/light/jsPilot.png +0 -0
  865. psychopy/app/Resources/light/jsPilot@2x.png +0 -0
  866. psychopy/app/Resources/light/jsRun.png +0 -0
  867. psychopy/app/Resources/light/jsRun@2x.png +0 -0
  868. psychopy/app/Resources/light/libroot16.png +0 -0
  869. psychopy/app/Resources/light/libroot16@2x.png +0 -0
  870. psychopy/app/Resources/light/loop.png +0 -0
  871. psychopy/app/Resources/light/loop@2x.png +0 -0
  872. psychopy/app/Resources/light/monitor16.png +0 -0
  873. psychopy/app/Resources/light/monitor16@2x.png +0 -0
  874. psychopy/app/Resources/light/monitors.png +0 -0
  875. psychopy/app/Resources/light/monitors32.png +0 -0
  876. psychopy/app/Resources/light/monitors32@2x.png +0 -0
  877. psychopy/app/Resources/light/monitors@2x.png +0 -0
  878. psychopy/app/Resources/light/orangedot.png +0 -0
  879. psychopy/app/Resources/light/orangedot@2x.png +0 -0
  880. psychopy/app/Resources/light/pavlovia.png +0 -0
  881. psychopy/app/Resources/light/pavlovia16.png +0 -0
  882. psychopy/app/Resources/light/pavlovia16@2x.png +0 -0
  883. psychopy/app/Resources/light/pavlovia@2x.png +0 -0
  884. psychopy/app/Resources/light/pavsync.png +0 -0
  885. psychopy/app/Resources/light/pavsync@2x.png +0 -0
  886. psychopy/app/Resources/light/person_off.png +0 -0
  887. psychopy/app/Resources/light/person_off@2x.png +0 -0
  888. psychopy/app/Resources/light/person_on.png +0 -0
  889. psychopy/app/Resources/light/person_on@2x.png +0 -0
  890. psychopy/app/Resources/light/photometer.png +0 -0
  891. psychopy/app/Resources/light/photometer@2x.png +0 -0
  892. psychopy/app/Resources/light/plugin16.png +0 -0
  893. psychopy/app/Resources/light/plugin16@2x.png +0 -0
  894. psychopy/app/Resources/light/plugins32.png +0 -0
  895. psychopy/app/Resources/light/plugins32@2x.png +0 -0
  896. psychopy/app/Resources/light/plus.png +0 -0
  897. psychopy/app/Resources/light/plus@2x.png +0 -0
  898. psychopy/app/Resources/light/preferences-app.png +0 -0
  899. psychopy/app/Resources/light/preferences-app48.png +0 -0
  900. psychopy/app/Resources/light/preferences-app48@2x.png +0 -0
  901. psychopy/app/Resources/light/preferences-app@2x.png +0 -0
  902. psychopy/app/Resources/light/preferences-conn.png +0 -0
  903. psychopy/app/Resources/light/preferences-conn48.png +0 -0
  904. psychopy/app/Resources/light/preferences-conn48@2x.png +0 -0
  905. psychopy/app/Resources/light/preferences-conn@2x.png +0 -0
  906. psychopy/app/Resources/light/preferences-general.png +0 -0
  907. psychopy/app/Resources/light/preferences-general48.png +0 -0
  908. psychopy/app/Resources/light/preferences-general48@2x.png +0 -0
  909. psychopy/app/Resources/light/preferences-general@2x.png +0 -0
  910. psychopy/app/Resources/light/preferences-hardware.png +0 -0
  911. psychopy/app/Resources/light/preferences-hardware48.png +0 -0
  912. psychopy/app/Resources/light/preferences-hardware48@2x.png +0 -0
  913. psychopy/app/Resources/light/preferences-hardware@2x.png +0 -0
  914. psychopy/app/Resources/light/preferences-keyboard.png +0 -0
  915. psychopy/app/Resources/light/preferences-keyboard48.png +0 -0
  916. psychopy/app/Resources/light/preferences-keyboard48@2x.png +0 -0
  917. psychopy/app/Resources/light/preferences-keyboard@2x.png +0 -0
  918. psychopy/app/Resources/light/preferences-pilot.png +0 -0
  919. psychopy/app/Resources/light/preferences-pilot@2x.png +0 -0
  920. psychopy/app/Resources/light/preferences32.png +0 -0
  921. psychopy/app/Resources/light/preferences32@2x.png +0 -0
  922. psychopy/app/Resources/light/pyPilot.png +0 -0
  923. psychopy/app/Resources/light/pyPilot@2x.png +0 -0
  924. psychopy/app/Resources/light/pyRun.png +0 -0
  925. psychopy/app/Resources/light/pyRun@2x.png +0 -0
  926. psychopy/app/Resources/light/reddot.png +0 -0
  927. psychopy/app/Resources/light/reddot@2x.png +0 -0
  928. psychopy/app/Resources/light/redglobe.png +0 -0
  929. psychopy/app/Resources/light/redglobe@2x.png +0 -0
  930. psychopy/app/Resources/light/redglobe_bug.png +0 -0
  931. psychopy/app/Resources/light/redglobe_bug@2x.png +0 -0
  932. psychopy/app/Resources/light/redglobe_greensync.png +0 -0
  933. psychopy/app/Resources/light/redglobe_greensync@2x.png +0 -0
  934. psychopy/app/Resources/light/redglobe_info.png +0 -0
  935. psychopy/app/Resources/light/redglobe_info@2x.png +0 -0
  936. psychopy/app/Resources/light/redglobe_magnifier.png +0 -0
  937. psychopy/app/Resources/light/redglobe_magnifier@2x.png +0 -0
  938. psychopy/app/Resources/light/redglobe_run.png +0 -0
  939. psychopy/app/Resources/light/redglobe_run@2x.png +0 -0
  940. psychopy/app/Resources/light/redglobe_user.png +0 -0
  941. psychopy/app/Resources/light/redglobe_user@2x.png +0 -0
  942. psychopy/app/Resources/light/redo.png +0 -0
  943. psychopy/app/Resources/light/redo32.png +0 -0
  944. psychopy/app/Resources/light/redo32@2x.png +0 -0
  945. psychopy/app/Resources/light/redo@2x.png +0 -0
  946. psychopy/app/Resources/light/regex.png +0 -0
  947. psychopy/app/Resources/light/regex@2x.png +0 -0
  948. psychopy/app/Resources/light/removeExp32.png +0 -0
  949. psychopy/app/Resources/light/removeExp32@2x.png +0 -0
  950. psychopy/app/Resources/light/rename16.png +0 -0
  951. psychopy/app/Resources/light/rename16@2x.png +0 -0
  952. psychopy/app/Resources/light/restart.png +0 -0
  953. psychopy/app/Resources/light/restart@2x.png +0 -0
  954. psychopy/app/Resources/light/runner.png +0 -0
  955. psychopy/app/Resources/light/runner@2x.png +0 -0
  956. psychopy/app/Resources/light/runnerPilot.png +0 -0
  957. psychopy/app/Resources/light/runnerPilot@2x.png +0 -0
  958. psychopy/app/Resources/light/savebtn16.png +0 -0
  959. psychopy/app/Resources/light/savebtn16@2x.png +0 -0
  960. psychopy/app/Resources/light/search.png +0 -0
  961. psychopy/app/Resources/light/search@2x.png +0 -0
  962. psychopy/app/Resources/light/showBuilder.png +0 -0
  963. psychopy/app/Resources/light/showBuilder@2x.png +0 -0
  964. psychopy/app/Resources/light/showCoder.png +0 -0
  965. psychopy/app/Resources/light/showCoder@2x.png +0 -0
  966. psychopy/app/Resources/light/showRunner.png +0 -0
  967. psychopy/app/Resources/light/showRunner@2x.png +0 -0
  968. psychopy/app/Resources/light/starred.png +0 -0
  969. psychopy/app/Resources/light/starred@2x.png +0 -0
  970. psychopy/app/Resources/light/start.png +0 -0
  971. psychopy/app/Resources/light/start@2x.png +0 -0
  972. psychopy/app/Resources/light/stdout.png +0 -0
  973. psychopy/app/Resources/light/stdout@2x.png +0 -0
  974. psychopy/app/Resources/light/stop.png +0 -0
  975. psychopy/app/Resources/light/stop32.png +0 -0
  976. psychopy/app/Resources/light/stop32@2x.png +0 -0
  977. psychopy/app/Resources/light/stop@2x.png +0 -0
  978. psychopy/app/Resources/light/switchCtrlBot.png +0 -0
  979. psychopy/app/Resources/light/switchCtrlBot@2x.png +0 -0
  980. psychopy/app/Resources/light/switchCtrlLeft.png +0 -0
  981. psychopy/app/Resources/light/switchCtrlLeft@2x.png +0 -0
  982. psychopy/app/Resources/light/switchCtrlRight.png +0 -0
  983. psychopy/app/Resources/light/switchCtrlRight@2x.png +0 -0
  984. psychopy/app/Resources/light/switchCtrlTop.png +0 -0
  985. psychopy/app/Resources/light/switchCtrlTop@2x.png +0 -0
  986. psychopy/app/Resources/light/tick.png +0 -0
  987. psychopy/app/Resources/light/tick@2x.png +0 -0
  988. psychopy/app/Resources/light/undo.png +0 -0
  989. psychopy/app/Resources/light/undo32.png +0 -0
  990. psychopy/app/Resources/light/undo32@2x.png +0 -0
  991. psychopy/app/Resources/light/undo@2x.png +0 -0
  992. psychopy/app/Resources/light/unstarred.png +0 -0
  993. psychopy/app/Resources/light/unstarred@2x.png +0 -0
  994. psychopy/app/Resources/light/user_none.png +0 -0
  995. psychopy/app/Resources/light/view-refresh16.png +0 -0
  996. psychopy/app/Resources/light/view-refresh16@2x.png +0 -0
  997. psychopy/app/Resources/light/viewbtn16.png +0 -0
  998. psychopy/app/Resources/light/viewbtn16@2x.png +0 -0
  999. psychopy/app/Resources/light/windows16.png +0 -0
  1000. psychopy/app/Resources/light/windows16@2x.png +0 -0
  1001. psychopy/app/Resources/moveComponentIcons.py +50 -0
  1002. psychopy/app/Resources/psychopy.desktop +13 -0
  1003. psychopy/app/Resources/psychopy.icns +0 -0
  1004. psychopy/app/Resources/psychopy.ico +0 -0
  1005. psychopy/app/Resources/psychopy.png +0 -0
  1006. psychopy/app/Resources/psychopy.xml +9 -0
  1007. psychopy/app/Resources/psychopy@2x.png +0 -0
  1008. psychopy/app/Resources/routine_templates/Basic.psyexp +344 -0
  1009. psychopy/app/Resources/routine_templates/Misc.psyexp +228 -0
  1010. psychopy/app/Resources/routine_templates/Online.psyexp +200 -0
  1011. psychopy/app/Resources/routine_templates/Trials.psyexp +735 -0
  1012. psychopy/app/Resources/routine_templates/readme.md +14 -0
  1013. psychopy/app/Resources/runner.ico +0 -0
  1014. psychopy/app/Resources/splash.png +0 -0
  1015. psychopy/app/Resources/splash@2x.png +0 -0
  1016. psychopy/app/Resources/tips.txt +45 -0
  1017. psychopy/app/Resources/tips_ar_001.txt +45 -0
  1018. psychopy/app/Resources/tips_fr_FR.txt +45 -0
  1019. psychopy/app/Resources/tips_ja_JP.txt +42 -0
  1020. psychopy/app/Resources/tips_zh_CN.txt +45 -0
  1021. psychopy/app/Resources/window.ico +0 -0
  1022. psychopy/app/__init__.py +339 -0
  1023. psychopy/app/__main__.py +3 -0
  1024. psychopy/app/_psychopyApp.py +1298 -0
  1025. psychopy/app/appData.spec +58 -0
  1026. psychopy/app/builder/__init__.py +5 -0
  1027. psychopy/app/builder/builder.py +4738 -0
  1028. psychopy/app/builder/dialogs/__init__.py +1910 -0
  1029. psychopy/app/builder/dialogs/dlgsCode.py +631 -0
  1030. psychopy/app/builder/dialogs/dlgsConditions.py +669 -0
  1031. psychopy/app/builder/dialogs/findDlg.py +275 -0
  1032. psychopy/app/builder/dialogs/paramCtrls.py +1802 -0
  1033. psychopy/app/builder/validators.py +589 -0
  1034. psychopy/app/coder/__init__.py +8 -0
  1035. psychopy/app/coder/codeEditorBase.py +460 -0
  1036. psychopy/app/coder/coder.py +3100 -0
  1037. psychopy/app/coder/fileBrowser.py +714 -0
  1038. psychopy/app/coder/folding.py +128 -0
  1039. psychopy/app/coder/psychoParser.py +139 -0
  1040. psychopy/app/coder/repl.py +496 -0
  1041. psychopy/app/coder/sourceTree.py +302 -0
  1042. psychopy/app/colorpicker/__init__.py +594 -0
  1043. psychopy/app/colorpicker/ui.py +336 -0
  1044. psychopy/app/connections/__init__.py +10 -0
  1045. psychopy/app/connections/news.py +104 -0
  1046. psychopy/app/connections/sendusage.py +61 -0
  1047. psychopy/app/connections/updates.py +642 -0
  1048. psychopy/app/console.py +164 -0
  1049. psychopy/app/deviceManager/__init__.py +1 -0
  1050. psychopy/app/deviceManager/addDialog.py +218 -0
  1051. psychopy/app/deviceManager/dialog.py +185 -0
  1052. psychopy/app/deviceManager/panel.py +191 -0
  1053. psychopy/app/deviceManager/utils.py +60 -0
  1054. psychopy/app/dialogs.py +664 -0
  1055. psychopy/app/errorDlg.py +238 -0
  1056. psychopy/app/frametracker.py +8 -0
  1057. psychopy/app/idle.py +181 -0
  1058. psychopy/app/jobs.py +676 -0
  1059. psychopy/app/linuxconfig/__init__.py +153 -0
  1060. psychopy/app/linuxconfig/ui.py +88 -0
  1061. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  1062. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +12695 -0
  1063. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  1064. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.po +10199 -0
  1065. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  1066. psychopy/app/locale/da_DK/LC_MESSAGE/messages.po +10199 -0
  1067. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  1068. psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +11221 -0
  1069. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  1070. psychopy/app/locale/el_GR/LC_MESSAGE/messages.po +10200 -0
  1071. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  1072. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.po +10200 -0
  1073. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  1074. psychopy/app/locale/en_US/LC_MESSAGE/messages.po +10195 -0
  1075. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  1076. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +11917 -0
  1077. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  1078. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +11924 -0
  1079. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  1080. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +11917 -0
  1081. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  1082. psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +11084 -0
  1083. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  1084. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +11590 -0
  1085. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  1086. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.po +10199 -0
  1087. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  1088. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +11091 -0
  1089. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  1090. psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +11072 -0
  1091. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  1092. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +11071 -0
  1093. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  1094. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.po +10200 -0
  1095. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  1096. psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +11072 -0
  1097. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  1098. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +11268 -0
  1099. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  1100. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.po +10199 -0
  1101. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  1102. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +11463 -0
  1103. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  1104. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.po +10200 -0
  1105. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  1106. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.po +10200 -0
  1107. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  1108. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.po +10200 -0
  1109. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  1110. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +11288 -0
  1111. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  1112. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.po +10200 -0
  1113. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  1114. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.po +10199 -0
  1115. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  1116. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +11441 -0
  1117. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  1118. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +11069 -0
  1119. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  1120. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +12085 -0
  1121. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  1122. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +11929 -0
  1123. psychopy/app/localizedStrings.py +386 -0
  1124. psychopy/app/pavlovia_ui/__init__.py +22 -0
  1125. psychopy/app/pavlovia_ui/_base.py +268 -0
  1126. psychopy/app/pavlovia_ui/functions.py +176 -0
  1127. psychopy/app/pavlovia_ui/menu.py +140 -0
  1128. psychopy/app/pavlovia_ui/project.py +943 -0
  1129. psychopy/app/pavlovia_ui/search.py +444 -0
  1130. psychopy/app/pavlovia_ui/sync.py +137 -0
  1131. psychopy/app/pavlovia_ui/user.py +264 -0
  1132. psychopy/app/plugin_manager/__init__.py +5 -0
  1133. psychopy/app/plugin_manager/dialog.py +402 -0
  1134. psychopy/app/plugin_manager/output.py +132 -0
  1135. psychopy/app/plugin_manager/packageIndex.py +303 -0
  1136. psychopy/app/plugin_manager/packages.py +643 -0
  1137. psychopy/app/plugin_manager/plugins.py +1363 -0
  1138. psychopy/app/plugin_manager/utils.py +115 -0
  1139. psychopy/app/preferencesDlg.py +771 -0
  1140. psychopy/app/psychopyApp.py +207 -0
  1141. psychopy/app/ribbon.py +1019 -0
  1142. psychopy/app/runner/__init__.py +1 -0
  1143. psychopy/app/runner/runner.py +1299 -0
  1144. psychopy/app/runner/scriptProcess.py +363 -0
  1145. psychopy/app/stdout/__init__.py +1 -0
  1146. psychopy/app/stdout/stdOutRich.py +410 -0
  1147. psychopy/app/sysInfoDlg.py +242 -0
  1148. psychopy/app/themes/__init__.py +78 -0
  1149. psychopy/app/themes/colors.py +201 -0
  1150. psychopy/app/themes/css/contrast_black.css +112 -0
  1151. psychopy/app/themes/css/contrast_white.css +115 -0
  1152. psychopy/app/themes/css/dark.css +112 -0
  1153. psychopy/app/themes/css/light.css +115 -0
  1154. psychopy/app/themes/fonts.py +629 -0
  1155. psychopy/app/themes/handlers.py +321 -0
  1156. psychopy/app/themes/icons.py +267 -0
  1157. psychopy/app/themes/spec/Classic.json +154 -0
  1158. psychopy/app/themes/spec/ClassicDark.json +154 -0
  1159. psychopy/app/themes/spec/GitHub.json +154 -0
  1160. psychopy/app/themes/spec/HiVisDark.json +153 -0
  1161. psychopy/app/themes/spec/HiVisLight.json +154 -0
  1162. psychopy/app/themes/spec/MinimalDark.json +154 -0
  1163. psychopy/app/themes/spec/MinimalLight.json +153 -0
  1164. psychopy/app/themes/spec/PsychopyDark.json +231 -0
  1165. psychopy/app/themes/spec/PsychopyLight.json +236 -0
  1166. psychopy/app/themes/ui.py +74 -0
  1167. psychopy/app/ui/__init__.py +184 -0
  1168. psychopy/app/urls.py +31 -0
  1169. psychopy/app/utils.py +1741 -0
  1170. psychopy/app/viewer/__init__.py +0 -0
  1171. psychopy/assets/Psychopy Window Favicon@16w.png +0 -0
  1172. psychopy/assets/Psychopy Window Favicon@32w.png +0 -0
  1173. psychopy/assets/USB-C.png +0 -0
  1174. psychopy/assets/USB.png +0 -0
  1175. psychopy/assets/__init__.py +0 -0
  1176. psychopy/assets/click.png +0 -0
  1177. psychopy/assets/clicknext.png +0 -0
  1178. psychopy/assets/creditCard.png +0 -0
  1179. psychopy/assets/default.mp3 +0 -0
  1180. psychopy/assets/default.mp4 +0 -0
  1181. psychopy/assets/default.png +0 -0
  1182. psychopy/assets/fonts/Arvo-Bold.ttf +0 -0
  1183. psychopy/assets/fonts/Arvo-BoldItalic.ttf +0 -0
  1184. psychopy/assets/fonts/Arvo-Italic.ttf +0 -0
  1185. psychopy/assets/fonts/Arvo-Regular.ttf +0 -0
  1186. psychopy/assets/fonts/DejaVuSerif.ttf +0 -0
  1187. psychopy/assets/fonts/IndieFlower-Regular.ttf +0 -0
  1188. psychopy/assets/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  1189. psychopy/assets/fonts/JetBrainsMono-VariableFont_wght.ttf +0 -0
  1190. psychopy/assets/fonts/NotoSans-Bold.ttf +0 -0
  1191. psychopy/assets/fonts/NotoSans-BoldItalic.ttf +0 -0
  1192. psychopy/assets/fonts/NotoSans-Italic.ttf +0 -0
  1193. psychopy/assets/fonts/NotoSans-Regular.ttf +0 -0
  1194. psychopy/assets/next.png +0 -0
  1195. psychopy/assets/psychopy.ico +0 -0
  1196. psychopy/assets/psychopy.png +0 -0
  1197. psychopy/assets/templates/__init__.py +0 -0
  1198. psychopy/assets/templates/instruct1.png +0 -0
  1199. psychopy/assets/templates/instruct2.png +0 -0
  1200. psychopy/assets/touch.png +0 -0
  1201. psychopy/assets/touchnext.png +0 -0
  1202. psychopy/assets/voicekeyThresholdStim.wav +0 -0
  1203. psychopy/assets/window.ico +0 -0
  1204. psychopy/changes/2023.1.0.md +9 -0
  1205. psychopy/changes/2024.1.0.md +16 -0
  1206. psychopy/changes/__init__.py +0 -0
  1207. psychopy/clock.py +619 -0
  1208. psychopy/colors.py +1058 -0
  1209. psychopy/compatibility.py +147 -0
  1210. psychopy/constants.py +87 -0
  1211. psychopy/contrib/__init__.py +0 -0
  1212. psychopy/contrib/configobj/LICENSE +39 -0
  1213. psychopy/contrib/configobj/__init__.py +2455 -0
  1214. psychopy/contrib/configobj/_version.py +2 -0
  1215. psychopy/contrib/configobj/validate.py +1458 -0
  1216. psychopy/contrib/lazy_import.py +402 -0
  1217. psychopy/contrib/mseq.py +273 -0
  1218. psychopy/contrib/mseqSearch.py +183 -0
  1219. psychopy/contrib/psi.py +100 -0
  1220. psychopy/contrib/quest.py +484 -0
  1221. psychopy/contrib/tesselate.py +188 -0
  1222. psychopy/core.py +167 -0
  1223. psychopy/data/__init__.py +45 -0
  1224. psychopy/data/base.py +566 -0
  1225. psychopy/data/counterbalance.py +210 -0
  1226. psychopy/data/experiment.py +1049 -0
  1227. psychopy/data/fit.py +248 -0
  1228. psychopy/data/routine.py +94 -0
  1229. psychopy/data/shelf.py +237 -0
  1230. psychopy/data/staircase.py +2270 -0
  1231. psychopy/data/trial.py +2505 -0
  1232. psychopy/data/utils.py +858 -0
  1233. psychopy/demos/__init__.py +0 -0
  1234. psychopy/demos/builder/Design Templates/branchedExperiment/README.md +5 -0
  1235. psychopy/demos/builder/Design Templates/branchedExperiment/branchedExperiment.psyexp +354 -0
  1236. psychopy/demos/builder/Design Templates/branchedExperiment/trialTypes.xlsx +0 -0
  1237. psychopy/demos/builder/Design Templates/psychophysicsStaircase/README.md +20 -0
  1238. psychopy/demos/builder/Design Templates/psychophysicsStaircase/psychophysicsStaircase.psyexp +286 -0
  1239. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/README.md +14 -0
  1240. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/psychophysicsStaircaseInterleaved.psyexp +340 -0
  1241. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/stairDefinitions.xlsx +0 -0
  1242. psychopy/demos/builder/Design Templates/randomisedBlocks/README.md +18 -0
  1243. psychopy/demos/builder/Design Templates/randomisedBlocks/chooseBlock.xlsx +0 -0
  1244. psychopy/demos/builder/Design Templates/randomisedBlocks/facesBlock.xlsx +0 -0
  1245. psychopy/demos/builder/Design Templates/randomisedBlocks/housesBlock.xlsx +0 -0
  1246. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.psyexp +219 -0
  1247. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face01.jpg +0 -0
  1248. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face02.jpg +0 -0
  1249. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/face03.jpg +0 -0
  1250. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house01.jpg +0 -0
  1251. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house02.jpg +0 -0
  1252. psychopy/demos/builder/Design Templates/randomisedBlocks/stims/house03.jpg +0 -0
  1253. psychopy/demos/builder/Experiments/BART/README.md +30 -0
  1254. psychopy/demos/builder/Experiments/BART/assets/background.jpg +0 -0
  1255. psychopy/demos/builder/Experiments/BART/assets/background.png +0 -0
  1256. psychopy/demos/builder/Experiments/BART/assets/bang.mp3 +0 -0
  1257. psychopy/demos/builder/Experiments/BART/assets/bang.wav +0 -0
  1258. psychopy/demos/builder/Experiments/BART/assets/blueBalloon.png +0 -0
  1259. psychopy/demos/builder/Experiments/BART/assets/greenBalloon.png +0 -0
  1260. psychopy/demos/builder/Experiments/BART/assets/redBalloon.png +0 -0
  1261. psychopy/demos/builder/Experiments/BART/bart.psyexp +844 -0
  1262. psychopy/demos/builder/Experiments/BART/spreadsheets/conditions.xlsx +0 -0
  1263. psychopy/demos/builder/Experiments/BigFiveInventory/BFI.psyexp +255 -0
  1264. psychopy/demos/builder/Experiments/BigFiveInventory/README.md +23 -0
  1265. psychopy/demos/builder/Experiments/BigFiveInventory/TIPI.xlsx +0 -0
  1266. psychopy/demos/builder/Experiments/BigFiveInventory/bigFiveItems.xlsx +0 -0
  1267. psychopy/demos/builder/Experiments/BigFiveInventory/demographics.xlsx +0 -0
  1268. psychopy/demos/builder/Experiments/BigFiveInventory/mini_IPIP.xlsx +0 -0
  1269. psychopy/demos/builder/Experiments/GoNoGo/conditions.xlsx +0 -0
  1270. psychopy/demos/builder/Experiments/GoNoGo/gng.psyexp +462 -0
  1271. psychopy/demos/builder/Experiments/GoNoGo/go.png +0 -0
  1272. psychopy/demos/builder/Experiments/GoNoGo/nogo.png +0 -0
  1273. psychopy/demos/builder/Experiments/dragAndDrop/README.md +5 -0
  1274. psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +494 -0
  1275. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/blank_grid.png +0 -0
  1276. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/make_shapes.psyexp +221 -0
  1277. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/readme.md +4 -0
  1278. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_1.png +0 -0
  1279. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_10.png +0 -0
  1280. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_2.png +0 -0
  1281. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_3.png +0 -0
  1282. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_4.png +0 -0
  1283. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_5.png +0 -0
  1284. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_6.png +0 -0
  1285. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_7.png +0 -0
  1286. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_8.png +0 -0
  1287. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_9.png +0 -0
  1288. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
  1289. psychopy/demos/builder/Experiments/goNoGo/readme.md +14 -0
  1290. psychopy/demos/builder/Experiments/mentalRotation/F.png +0 -0
  1291. psychopy/demos/builder/Experiments/mentalRotation/FR.png +0 -0
  1292. psychopy/demos/builder/Experiments/mentalRotation/MentalRot.csv +33 -0
  1293. psychopy/demos/builder/Experiments/mentalRotation/MentalRotation.psyexp +646 -0
  1294. psychopy/demos/builder/Experiments/mentalRotation/README.md +23 -0
  1295. psychopy/demos/builder/Experiments/navon/NavonTask.psyexp +509 -0
  1296. psychopy/demos/builder/Experiments/navon/README.md +26 -0
  1297. psychopy/demos/builder/Experiments/navon/bigHsmallH.png +0 -0
  1298. psychopy/demos/builder/Experiments/navon/bigHsmallS.png +0 -0
  1299. psychopy/demos/builder/Experiments/navon/bigSsmallH.png +0 -0
  1300. psychopy/demos/builder/Experiments/navon/bigSsmallS.png +0 -0
  1301. psychopy/demos/builder/Experiments/navon/mask.png +0 -0
  1302. psychopy/demos/builder/Experiments/navon/stimuli.pptx +0 -0
  1303. psychopy/demos/builder/Experiments/navon/trialTypes.xlsx +0 -0
  1304. psychopy/demos/builder/Experiments/sternberg/README.md +14 -0
  1305. psychopy/demos/builder/Experiments/sternberg/mainTrials.xlsx +0 -0
  1306. psychopy/demos/builder/Experiments/sternberg/pracTrials.xlsx +0 -0
  1307. psychopy/demos/builder/Experiments/sternberg/sternberg.psyexp +649 -0
  1308. psychopy/demos/builder/Experiments/stroop/README.md +15 -0
  1309. psychopy/demos/builder/Experiments/stroop/stroop.psyexp +321 -0
  1310. psychopy/demos/builder/Experiments/stroop/trialTypes.csv +7 -0
  1311. psychopy/demos/builder/Experiments/stroopExtended/README.md +14 -0
  1312. psychopy/demos/builder/Experiments/stroopExtended/stroop.psyexp +418 -0
  1313. psychopy/demos/builder/Experiments/stroopExtended/stroopReverse.psyexp +418 -0
  1314. psychopy/demos/builder/Experiments/stroopExtended/trialTypes.xlsx +0 -0
  1315. psychopy/demos/builder/Experiments/stroopExtended/trialTypesReverse.xlsx +0 -0
  1316. psychopy/demos/builder/Experiments/stroopVoice/README.md +27 -0
  1317. psychopy/demos/builder/Experiments/stroopVoice/conditions.xlsx +0 -0
  1318. psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +382 -0
  1319. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +371 -0
  1320. psychopy/demos/builder/Feature Demos/buttonBox/readme.md +5 -0
  1321. psychopy/demos/builder/Feature Demos/counterbalance/counterbalance.psyexp +308 -0
  1322. psychopy/demos/builder/Feature Demos/gratings/gratings.psyexp +391 -0
  1323. psychopy/demos/builder/Feature Demos/gratings/readme.md +8 -0
  1324. psychopy/demos/builder/Feature Demos/movies/movie.psyexp +220 -0
  1325. psychopy/demos/builder/Feature Demos/movies/readme.md +3 -0
  1326. psychopy/demos/builder/Feature Demos/noise/face.jpg +0 -0
  1327. psychopy/demos/builder/Feature Demos/noise/noise.psyexp +477 -0
  1328. psychopy/demos/builder/Feature Demos/noise/readme.md +3 -0
  1329. psychopy/demos/builder/Feature Demos/panorama/panImg.jpg +0 -0
  1330. psychopy/demos/builder/Feature Demos/panorama/panorama.psyexp +177 -0
  1331. psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +434 -0
  1332. psychopy/demos/builder/Feature Demos/pilotMode/readme.md +7 -0
  1333. psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +451 -0
  1334. psychopy/demos/builder/Feature Demos/progress/readme.md +1 -0
  1335. psychopy/demos/builder/Feature Demos/sliders/README.md +8 -0
  1336. psychopy/demos/builder/Feature Demos/sliders/fruitConditions.xlsx +0 -0
  1337. psychopy/demos/builder/Feature Demos/sliders/sliders.psyexp +984 -0
  1338. psychopy/demos/builder/Feature Demos/visualValidator/readme.md +7 -0
  1339. psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +199 -0
  1340. psychopy/demos/builder/Hardware/EEG_parallel_component/EEG_triggers_parallel_comp.psyexp +639 -0
  1341. psychopy/demos/builder/Hardware/EEG_serial_code/EEG_triggers_serial_code.psyexp +415 -0
  1342. psychopy/demos/builder/Hardware/EEG_serial_component/EEG_triggers_serial_comp.psyexp +659 -0
  1343. psychopy/demos/builder/Hardware/EGI_netstation/README.md +17 -0
  1344. psychopy/demos/builder/Hardware/EGI_netstation/stroop.psyexp +339 -0
  1345. psychopy/demos/builder/Hardware/EGI_netstation/trialTypesEEG.csv +7 -0
  1346. psychopy/demos/builder/Hardware/Eyetracking_visual_search/readme.md +19 -0
  1347. psychopy/demos/builder/Hardware/Eyetracking_visual_search/trials_params.xlsx +0 -0
  1348. psychopy/demos/builder/Hardware/Eyetracking_visual_search/visualSearch.psyexp +830 -0
  1349. psychopy/demos/builder/Hardware/camera/camera.psyexp +330 -0
  1350. psychopy/demos/builder/Hardware/camera/readme.md +6 -0
  1351. psychopy/demos/builder/Hardware/eyetracking/eyetracking.psyexp +459 -0
  1352. psychopy/demos/builder/Hardware/eyetracking/readme.md +7 -0
  1353. psychopy/demos/builder/Hardware/eyetracking_custom_cal/Bullseye_grey.mov +0 -0
  1354. psychopy/demos/builder/Hardware/eyetracking_custom_cal/eyetracking_custom_cal.psyexp +468 -0
  1355. psychopy/demos/builder/Hardware/eyetracking_custom_cal/readme.md +7 -0
  1356. psychopy/demos/builder/Hardware/fMRI/fMRI_demo.psyexp +211 -0
  1357. psychopy/demos/builder/Hardware/fMRI/readme.md +7 -0
  1358. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo.psyexp +360 -0
  1359. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy.psyexp +360 -0
  1360. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy_legacy.psyexp +312 -0
  1361. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy.psyexp +360 -0
  1362. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy.psyexp +360 -0
  1363. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy_legacy.psyexp +312 -0
  1364. psychopy/demos/builder/Hardware/microphone/microphone.psyexp +484 -0
  1365. psychopy/demos/builder/Hardware/microphone/phrases.xlsx +0 -0
  1366. psychopy/demos/builder/Hardware/microphone/readme.md +9 -0
  1367. psychopy/demos/builder/Hardware/pump/README.md +19 -0
  1368. psychopy/demos/builder/Hardware/pump/pump.psyexp +646 -0
  1369. psychopy/demos/builder/Helper Tools/achorVSalignment/FlowCircular-Regular.ttf +0 -0
  1370. psychopy/demos/builder/Helper Tools/achorVSalignment/anchorAlignment.psyexp +350 -0
  1371. psychopy/demos/builder/Helper Tools/achorVSalignment/readme.md +5 -0
  1372. psychopy/demos/builder/Helper Tools/clockFace/README.md +3 -0
  1373. psychopy/demos/builder/Helper Tools/clockFace/clockFace.psyexp +211 -0
  1374. psychopy/demos/builder/Helper Tools/colors/README.md +5 -0
  1375. psychopy/demos/builder/Helper Tools/colors/colors.psyexp +321 -0
  1376. psychopy/demos/builder/Helper Tools/drawPolygon/README.md +5 -0
  1377. psychopy/demos/builder/Helper Tools/drawPolygon/drawPolygon.psyexp +704 -0
  1378. psychopy/demos/builder/Helper Tools/keyNameFinder/README.md +14 -0
  1379. psychopy/demos/builder/Helper Tools/keyNameFinder/keyNameFinder.psyexp +225 -0
  1380. psychopy/demos/builder/Helper Tools/spatialUnits/README.md +6 -0
  1381. psychopy/demos/builder/Helper Tools/spatialUnits/unitDemo.psyexp +206 -0
  1382. psychopy/demos/builder/README.txt +43 -0
  1383. psychopy/demos/builder/__init__.py +0 -0
  1384. psychopy/demos/coder/__init__.py +0 -0
  1385. psychopy/demos/coder/basic/hello_world.py +37 -0
  1386. psychopy/demos/coder/csvFromPsydat.py +29 -0
  1387. psychopy/demos/coder/experiment control/JND_staircase_analysis.py +85 -0
  1388. psychopy/demos/coder/experiment control/JND_staircase_exp.py +110 -0
  1389. psychopy/demos/coder/experiment control/TrialHandler.py +64 -0
  1390. psychopy/demos/coder/experiment control/TrialHandler2.py +42 -0
  1391. psychopy/demos/coder/experiment control/__init__.py +0 -0
  1392. psychopy/demos/coder/experiment control/autoDraw_autoLog.py +50 -0
  1393. psychopy/demos/coder/experiment control/experimentHandler.py +57 -0
  1394. psychopy/demos/coder/experiment control/fMRI_launchScan.py +74 -0
  1395. psychopy/demos/coder/experiment control/gammaMotionAnalysis.py +68 -0
  1396. psychopy/demos/coder/experiment control/gammaMotionNull.py +169 -0
  1397. psychopy/demos/coder/experiment control/logFiles.py +55 -0
  1398. psychopy/demos/coder/experiment control/piloting.py +65 -0
  1399. psychopy/demos/coder/experiment control/runtimeInfo.py +79 -0
  1400. psychopy/demos/coder/hardware/CRS_BitsBox.py +75 -0
  1401. psychopy/demos/coder/hardware/CRS_BitsPlusPlus.py +73 -0
  1402. psychopy/demos/coder/hardware/RiftHeadTrackingExample.py +95 -0
  1403. psychopy/demos/coder/hardware/RiftMinimal.py +44 -0
  1404. psychopy/demos/coder/hardware/VSHD_Distortion.py +49 -0
  1405. psychopy/demos/coder/hardware/__init__.py +0 -0
  1406. psychopy/demos/coder/hardware/camera.py +90 -0
  1407. psychopy/demos/coder/hardware/cedrusRB730.py +38 -0
  1408. psychopy/demos/coder/hardware/crsBitsAdvancedDemo.py +780 -0
  1409. psychopy/demos/coder/hardware/egi_netstation.py +45 -0
  1410. psychopy/demos/coder/hardware/hdf5_extract.py +133 -0
  1411. psychopy/demos/coder/hardware/ioLab_bbox.py +62 -0
  1412. psychopy/demos/coder/hardware/labjack_u3.py +61 -0
  1413. psychopy/demos/coder/hardware/monitorDemo.py +28 -0
  1414. psychopy/demos/coder/hardware/parallelPortOutput.py +49 -0
  1415. psychopy/demos/coder/hardware/qmixPump.py +79 -0
  1416. psychopy/demos/coder/hardware/testSoundLatency.py +127 -0
  1417. psychopy/demos/coder/input/GUI.py +52 -0
  1418. psychopy/demos/coder/input/__init__.py +0 -0
  1419. psychopy/demos/coder/input/customMouse.py +51 -0
  1420. psychopy/demos/coder/input/joystick_universal.py +84 -0
  1421. psychopy/demos/coder/input/keyNameFinder.py +39 -0
  1422. psychopy/demos/coder/input/mic.png +0 -0
  1423. psychopy/demos/coder/input/mouse.py +66 -0
  1424. psychopy/demos/coder/iohub/delaytest.py +231 -0
  1425. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/canal.jpg +0 -0
  1426. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/fall.jpg +0 -0
  1427. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/lake.jpg +0 -0
  1428. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/party.jpg +0 -0
  1429. psychopy/demos/coder/iohub/eyetracking/gcCursor/images/swimming.jpg +0 -0
  1430. psychopy/demos/coder/iohub/eyetracking/gcCursor/readTrialEventsByConditionVariables.py +67 -0
  1431. psychopy/demos/coder/iohub/eyetracking/gcCursor/readTrialEventsByMessages.py +34 -0
  1432. psychopy/demos/coder/iohub/eyetracking/gcCursor/run.py +323 -0
  1433. psychopy/demos/coder/iohub/eyetracking/gcCursor/trial_conditions.xlsx +0 -0
  1434. psychopy/demos/coder/iohub/eyetracking/simple.py +147 -0
  1435. psychopy/demos/coder/iohub/eyetracking/validation.py +251 -0
  1436. psychopy/demos/coder/iohub/iodatastore/saveEventReport.py +56 -0
  1437. psychopy/demos/coder/iohub/keyboard.py +189 -0
  1438. psychopy/demos/coder/iohub/keyboardreactiontime.py +88 -0
  1439. psychopy/demos/coder/iohub/launchHub.py +160 -0
  1440. psychopy/demos/coder/iohub/mouse.py +112 -0
  1441. psychopy/demos/coder/iohub/mouse_multi_window.py +124 -0
  1442. psychopy/demos/coder/iohub/serial/_parseserial.py +53 -0
  1443. psychopy/demos/coder/iohub/serial/customparser.py +84 -0
  1444. psychopy/demos/coder/iohub/serial/pstbox.py +166 -0
  1445. psychopy/demos/coder/iohub/wintab/_wintabgraphics.py +281 -0
  1446. psychopy/demos/coder/iohub/wintab/pen_demo.py +260 -0
  1447. psychopy/demos/coder/misc/encrypt_data.py +48 -0
  1448. psychopy/demos/coder/misc/hdf5_2_csv +33 -0
  1449. psychopy/demos/coder/misc/makeMovie.py +28 -0
  1450. psychopy/demos/coder/misc/rigidBodyTransform.py +76 -0
  1451. psychopy/demos/coder/stimuli/Campaign.ttf +0 -0
  1452. psychopy/demos/coder/stimuli/MovieStim.py +65 -0
  1453. psychopy/demos/coder/stimuli/__init__.py +0 -0
  1454. psychopy/demos/coder/stimuli/aperture.py +37 -0
  1455. psychopy/demos/coder/stimuli/beach.jpg +0 -0
  1456. psychopy/demos/coder/stimuli/bufferImageStim.py +78 -0
  1457. psychopy/demos/coder/stimuli/clockface.py +43 -0
  1458. psychopy/demos/coder/stimuli/colorPalette.py +134 -0
  1459. psychopy/demos/coder/stimuli/compare_text_timing.py +193 -0
  1460. psychopy/demos/coder/stimuli/counterphase.py +43 -0
  1461. psychopy/demos/coder/stimuli/customTextures.py +57 -0
  1462. psychopy/demos/coder/stimuli/dot_gabors.py +35 -0
  1463. psychopy/demos/coder/stimuli/dots.py +35 -0
  1464. psychopy/demos/coder/stimuli/elementArrays.py +92 -0
  1465. psychopy/demos/coder/stimuli/embeddedOpenGL.py +35 -0
  1466. psychopy/demos/coder/stimuli/face.jpg +0 -0
  1467. psychopy/demos/coder/stimuli/face_jpg.py +48 -0
  1468. psychopy/demos/coder/stimuli/gabor.py +30 -0
  1469. psychopy/demos/coder/stimuli/imagesAndPatches.py +51 -0
  1470. psychopy/demos/coder/stimuli/jwpIntro.mp4 +0 -0
  1471. psychopy/demos/coder/stimuli/kanizsa.py +41 -0
  1472. psychopy/demos/coder/stimuli/maskReveal.py +63 -0
  1473. psychopy/demos/coder/stimuli/plaid.py +43 -0
  1474. psychopy/demos/coder/stimuli/ratingScale.py +183 -0
  1475. psychopy/demos/coder/stimuli/rotatingFlashingWedge.py +37 -0
  1476. psychopy/demos/coder/stimuli/screensAndWindows.py +58 -0
  1477. psychopy/demos/coder/stimuli/secondOrderGratings.py +66 -0
  1478. psychopy/demos/coder/stimuli/shapeContains.py +50 -0
  1479. psychopy/demos/coder/stimuli/shapes.py +63 -0
  1480. psychopy/demos/coder/stimuli/soundStimuli.py +49 -0
  1481. psychopy/demos/coder/stimuli/starField.py +48 -0
  1482. psychopy/demos/coder/stimuli/stim3d.py +107 -0
  1483. psychopy/demos/coder/stimuli/textBoxStim/textbox_glyph_placement.py +93 -0
  1484. psychopy/demos/coder/stimuli/textBoxStim/textbox_simple.py +71 -0
  1485. psychopy/demos/coder/stimuli/textStimuli.py +106 -0
  1486. psychopy/demos/coder/stimuli/textbox_editable.py +64 -0
  1487. psychopy/demos/coder/stimuli/variousVisualStims.py +47 -0
  1488. psychopy/demos/coder/stimuli/visual_noise.py +31 -0
  1489. psychopy/demos/coder/sysInfo.py +56 -0
  1490. psychopy/demos/coder/timing/__init__.py +0 -0
  1491. psychopy/demos/coder/timing/callOnFlip.py +39 -0
  1492. psychopy/demos/coder/timing/clocksAndTimers.py +46 -0
  1493. psychopy/demos/coder/timing/millikeyKeyboardTimingTest.py +323 -0
  1494. psychopy/demos/coder/timing/timeByFrames.py +80 -0
  1495. psychopy/demos/coder/timing/timeByFramesEx.py +107 -0
  1496. psychopy/demos/coder/understanding psychopy/colors.py +70 -0
  1497. psychopy/demos/coder/understanding psychopy/fontLayout.py +69 -0
  1498. psychopy/demos/demo_migration.py +214 -0
  1499. psychopy/demos/modernizeDemos.py +16 -0
  1500. psychopy/demos/test_demo_migration.py +155 -0
  1501. psychopy/devices/__init__.py +8 -0
  1502. psychopy/event.py +1339 -0
  1503. psychopy/exceptions.py +59 -0
  1504. psychopy/experiment/__init__.py +51 -0
  1505. psychopy/experiment/_experiment.py +1514 -0
  1506. psychopy/experiment/blankTemplate.xltx +0 -0
  1507. psychopy/experiment/components/__init__.py +402 -0
  1508. psychopy/experiment/components/_base.py +1689 -0
  1509. psychopy/experiment/components/aperture/__init__.py +153 -0
  1510. psychopy/experiment/components/aperture/classic/aperture.png +0 -0
  1511. psychopy/experiment/components/aperture/classic/aperture@2x.png +0 -0
  1512. psychopy/experiment/components/aperture/dark/aperture.png +0 -0
  1513. psychopy/experiment/components/aperture/dark/aperture@2x.png +0 -0
  1514. psychopy/experiment/components/aperture/light/aperture.png +0 -0
  1515. psychopy/experiment/components/aperture/light/aperture@2x.png +0 -0
  1516. psychopy/experiment/components/brush/__init__.py +165 -0
  1517. psychopy/experiment/components/brush/classic/brush.png +0 -0
  1518. psychopy/experiment/components/brush/classic/brush@2x.png +0 -0
  1519. psychopy/experiment/components/brush/dark/brush.png +0 -0
  1520. psychopy/experiment/components/brush/dark/brush@2x.png +0 -0
  1521. psychopy/experiment/components/brush/light/brush.png +0 -0
  1522. psychopy/experiment/components/brush/light/brush@2x.png +0 -0
  1523. psychopy/experiment/components/button/__init__.py +493 -0
  1524. psychopy/experiment/components/button/classic/button.png +0 -0
  1525. psychopy/experiment/components/button/classic/button@2x.png +0 -0
  1526. psychopy/experiment/components/button/dark/button.png +0 -0
  1527. psychopy/experiment/components/button/dark/button@2x.png +0 -0
  1528. psychopy/experiment/components/button/light/button.png +0 -0
  1529. psychopy/experiment/components/button/light/button@2x.png +0 -0
  1530. psychopy/experiment/components/buttonBox/__init__.py +322 -0
  1531. psychopy/experiment/components/buttonBox/classic/buttonBox.png +0 -0
  1532. psychopy/experiment/components/buttonBox/classic/buttonBox@2x.png +0 -0
  1533. psychopy/experiment/components/buttonBox/dark/buttonBox.png +0 -0
  1534. psychopy/experiment/components/buttonBox/dark/buttonBox@2x.png +0 -0
  1535. psychopy/experiment/components/buttonBox/light/buttonBox.png +0 -0
  1536. psychopy/experiment/components/buttonBox/light/buttonBox@2x.png +0 -0
  1537. psychopy/experiment/components/camera/__init__.py +381 -0
  1538. psychopy/experiment/components/camera/classic/webcam.png +0 -0
  1539. psychopy/experiment/components/camera/classic/webcam@2x.png +0 -0
  1540. psychopy/experiment/components/camera/dark/webcam.png +0 -0
  1541. psychopy/experiment/components/camera/dark/webcam@2x.png +0 -0
  1542. psychopy/experiment/components/camera/light/webcam.png +0 -0
  1543. psychopy/experiment/components/camera/light/webcam@2x.png +0 -0
  1544. psychopy/experiment/components/code/__init__.py +299 -0
  1545. psychopy/experiment/components/code/classic/code.png +0 -0
  1546. psychopy/experiment/components/code/classic/code@2x.png +0 -0
  1547. psychopy/experiment/components/code/dark/code.png +0 -0
  1548. psychopy/experiment/components/code/dark/code@2x.png +0 -0
  1549. psychopy/experiment/components/code/light/code.png +0 -0
  1550. psychopy/experiment/components/code/light/code@2x.png +0 -0
  1551. psychopy/experiment/components/dots/__init__.py +199 -0
  1552. psychopy/experiment/components/dots/classic/dots.png +0 -0
  1553. psychopy/experiment/components/dots/classic/dots@2x.png +0 -0
  1554. psychopy/experiment/components/dots/dark/dots.png +0 -0
  1555. psychopy/experiment/components/dots/dark/dots@2x.png +0 -0
  1556. psychopy/experiment/components/dots/dots.xcf +0 -0
  1557. psychopy/experiment/components/dots/light/dots.png +0 -0
  1558. psychopy/experiment/components/dots/light/dots@2x.png +0 -0
  1559. psychopy/experiment/components/eyetracker_record/__init__.py +194 -0
  1560. psychopy/experiment/components/eyetracker_record/classic/eyetracker_record.png +0 -0
  1561. psychopy/experiment/components/eyetracker_record/classic/eyetracker_record@2x.png +0 -0
  1562. psychopy/experiment/components/eyetracker_record/dark/eyetracker_record.png +0 -0
  1563. psychopy/experiment/components/eyetracker_record/dark/eyetracker_record@2x.png +0 -0
  1564. psychopy/experiment/components/eyetracker_record/light/eyetracker_record.png +0 -0
  1565. psychopy/experiment/components/eyetracker_record/light/eyetracker_record@2x.png +0 -0
  1566. psychopy/experiment/components/form/__init__.py +227 -0
  1567. psychopy/experiment/components/form/classic/form.png +0 -0
  1568. psychopy/experiment/components/form/classic/form@2x.png +0 -0
  1569. psychopy/experiment/components/form/dark/form.png +0 -0
  1570. psychopy/experiment/components/form/dark/form@2x.png +0 -0
  1571. psychopy/experiment/components/form/formItems.xltx +0 -0
  1572. psychopy/experiment/components/form/light/form.png +0 -0
  1573. psychopy/experiment/components/form/light/form@2x.png +0 -0
  1574. psychopy/experiment/components/grating/__init__.py +203 -0
  1575. psychopy/experiment/components/grating/classic/grating.png +0 -0
  1576. psychopy/experiment/components/grating/classic/grating@2x.png +0 -0
  1577. psychopy/experiment/components/grating/dark/grating.png +0 -0
  1578. psychopy/experiment/components/grating/dark/grating@2x.png +0 -0
  1579. psychopy/experiment/components/grating/light/grating.png +0 -0
  1580. psychopy/experiment/components/grating/light/grating@2x.png +0 -0
  1581. psychopy/experiment/components/image/__init__.py +195 -0
  1582. psychopy/experiment/components/image/classic/image.png +0 -0
  1583. psychopy/experiment/components/image/classic/image@2x.png +0 -0
  1584. psychopy/experiment/components/image/dark/image.png +0 -0
  1585. psychopy/experiment/components/image/dark/image@2x.png +0 -0
  1586. psychopy/experiment/components/image/light/image.png +0 -0
  1587. psychopy/experiment/components/image/light/image@2x.png +0 -0
  1588. psychopy/experiment/components/joyButtons/__init__.py +453 -0
  1589. psychopy/experiment/components/joyButtons/classic/joyButtons.png +0 -0
  1590. psychopy/experiment/components/joyButtons/classic/joybuttons@2x.png +0 -0
  1591. psychopy/experiment/components/joyButtons/dark/joyButtons.png +0 -0
  1592. psychopy/experiment/components/joyButtons/dark/joyButtons@2x.png +0 -0
  1593. psychopy/experiment/components/joyButtons/light/joyButtons.png +0 -0
  1594. psychopy/experiment/components/joyButtons/light/joyButtons@2x.png +0 -0
  1595. psychopy/experiment/components/joyButtons/virtualJoyButtons.py +34 -0
  1596. psychopy/experiment/components/joystick/__init__.py +572 -0
  1597. psychopy/experiment/components/joystick/classic/joystick.png +0 -0
  1598. psychopy/experiment/components/joystick/classic/joystick@2x.png +0 -0
  1599. psychopy/experiment/components/joystick/dark/joystick.png +0 -0
  1600. psychopy/experiment/components/joystick/dark/joystick@2x.png +0 -0
  1601. psychopy/experiment/components/joystick/light/joystick.png +0 -0
  1602. psychopy/experiment/components/joystick/light/joystick@2x.png +0 -0
  1603. psychopy/experiment/components/joystick/virtualJoystick.py +40 -0
  1604. psychopy/experiment/components/keyboard/__init__.py +568 -0
  1605. psychopy/experiment/components/keyboard/classic/keyboard.png +0 -0
  1606. psychopy/experiment/components/keyboard/classic/keyboard@2x.png +0 -0
  1607. psychopy/experiment/components/keyboard/dark/keyboard.png +0 -0
  1608. psychopy/experiment/components/keyboard/dark/keyboard@2x.png +0 -0
  1609. psychopy/experiment/components/keyboard/light/keyboard.png +0 -0
  1610. psychopy/experiment/components/keyboard/light/keyboard@2x.png +0 -0
  1611. psychopy/experiment/components/keyboard.xcf +0 -0
  1612. psychopy/experiment/components/microphone/__init__.py +600 -0
  1613. psychopy/experiment/components/microphone/classic/microphone.png +0 -0
  1614. psychopy/experiment/components/microphone/classic/microphone@2x.png +0 -0
  1615. psychopy/experiment/components/microphone/dark/microphone.png +0 -0
  1616. psychopy/experiment/components/microphone/dark/microphone@2x.png +0 -0
  1617. psychopy/experiment/components/microphone/light/microphone.png +0 -0
  1618. psychopy/experiment/components/microphone/light/microphone@2x.png +0 -0
  1619. psychopy/experiment/components/mouse/__init__.py +773 -0
  1620. psychopy/experiment/components/mouse/classic/mouse.png +0 -0
  1621. psychopy/experiment/components/mouse/classic/mouse@2x.png +0 -0
  1622. psychopy/experiment/components/mouse/dark/mouse.png +0 -0
  1623. psychopy/experiment/components/mouse/dark/mouse@2x.png +0 -0
  1624. psychopy/experiment/components/mouse/light/mouse.png +0 -0
  1625. psychopy/experiment/components/mouse/light/mouse@2x.png +0 -0
  1626. psychopy/experiment/components/movie/__init__.py +348 -0
  1627. psychopy/experiment/components/movie/classic/movie.png +0 -0
  1628. psychopy/experiment/components/movie/classic/movie@2x.png +0 -0
  1629. psychopy/experiment/components/movie/dark/movie.png +0 -0
  1630. psychopy/experiment/components/movie/dark/movie@2x.png +0 -0
  1631. psychopy/experiment/components/movie/light/movie.png +0 -0
  1632. psychopy/experiment/components/movie/light/movie@2x.png +0 -0
  1633. psychopy/experiment/components/panorama/__init__.py +456 -0
  1634. psychopy/experiment/components/panorama/classic/panorama.png +0 -0
  1635. psychopy/experiment/components/panorama/classic/panorama@2x.png +0 -0
  1636. psychopy/experiment/components/panorama/dark/panorama.png +0 -0
  1637. psychopy/experiment/components/panorama/dark/panorama@2x.png +0 -0
  1638. psychopy/experiment/components/panorama/light/panorama.png +0 -0
  1639. psychopy/experiment/components/panorama/light/panorama@2x.png +0 -0
  1640. psychopy/experiment/components/parallelOut/__init__.py +178 -0
  1641. psychopy/experiment/components/parallelOut/classic/parallel.png +0 -0
  1642. psychopy/experiment/components/parallelOut/classic/parallel@2x.png +0 -0
  1643. psychopy/experiment/components/parallelOut/dark/parallel.png +0 -0
  1644. psychopy/experiment/components/parallelOut/dark/parallel@2x.png +0 -0
  1645. psychopy/experiment/components/parallelOut/light/parallel.png +0 -0
  1646. psychopy/experiment/components/parallelOut/light/parallel@2x.png +0 -0
  1647. psychopy/experiment/components/polygon/__init__.py +332 -0
  1648. psychopy/experiment/components/polygon/classic/polygon.png +0 -0
  1649. psychopy/experiment/components/polygon/classic/polygon@2x.png +0 -0
  1650. psychopy/experiment/components/polygon/dark/polygon.png +0 -0
  1651. psychopy/experiment/components/polygon/dark/polygon@2x.png +0 -0
  1652. psychopy/experiment/components/polygon/light/polygon.png +0 -0
  1653. psychopy/experiment/components/polygon/light/polygon@2x.png +0 -0
  1654. psychopy/experiment/components/progress/__init__.py +130 -0
  1655. psychopy/experiment/components/progress/classic/progress.png +0 -0
  1656. psychopy/experiment/components/progress/classic/progress@2x.png +0 -0
  1657. psychopy/experiment/components/progress/dark/progress.png +0 -0
  1658. psychopy/experiment/components/progress/dark/progress@2x.png +0 -0
  1659. psychopy/experiment/components/progress/light/progress.png +0 -0
  1660. psychopy/experiment/components/progress/light/progress@2x.png +0 -0
  1661. psychopy/experiment/components/resourceManager/__init__.py +176 -0
  1662. psychopy/experiment/components/resourceManager/classic/resource_manager.png +0 -0
  1663. psychopy/experiment/components/resourceManager/classic/resource_manager@2x.png +0 -0
  1664. psychopy/experiment/components/resourceManager/dark/resource_manager.png +0 -0
  1665. psychopy/experiment/components/resourceManager/dark/resource_manager@2x.png +0 -0
  1666. psychopy/experiment/components/resourceManager/light/resource_manager.png +0 -0
  1667. psychopy/experiment/components/resourceManager/light/resource_manager@2x.png +0 -0
  1668. psychopy/experiment/components/roi/__init__.py +316 -0
  1669. psychopy/experiment/components/roi/classic/eyetracker_roi.png +0 -0
  1670. psychopy/experiment/components/roi/classic/eyetracker_roi@2x.png +0 -0
  1671. psychopy/experiment/components/roi/dark/eyetracker_roi.png +0 -0
  1672. psychopy/experiment/components/roi/dark/eyetracker_roi@2X.png +0 -0
  1673. psychopy/experiment/components/roi/light/eyetracker_roi.png +0 -0
  1674. psychopy/experiment/components/roi/light/eyetracker_roi@2X.png +0 -0
  1675. psychopy/experiment/components/routineSettings/__init__.py +387 -0
  1676. psychopy/experiment/components/routineSettings/classic/routineSettings.png +0 -0
  1677. psychopy/experiment/components/routineSettings/classic/routineSettings@2x.png +0 -0
  1678. psychopy/experiment/components/routineSettings/dark/routineSettings.png +0 -0
  1679. psychopy/experiment/components/routineSettings/dark/routineSettings@2x.png +0 -0
  1680. psychopy/experiment/components/routineSettings/light/routineSettings.png +0 -0
  1681. psychopy/experiment/components/routineSettings/light/routineSettings@2x.png +0 -0
  1682. psychopy/experiment/components/serialOut/__init__.py +304 -0
  1683. psychopy/experiment/components/serialOut/classic/serial.png +0 -0
  1684. psychopy/experiment/components/serialOut/classic/serial@2x.png +0 -0
  1685. psychopy/experiment/components/serialOut/dark/serial.png +0 -0
  1686. psychopy/experiment/components/serialOut/dark/serial@2x.png +0 -0
  1687. psychopy/experiment/components/serialOut/light/serial.png +0 -0
  1688. psychopy/experiment/components/serialOut/light/serial@2x.png +0 -0
  1689. psychopy/experiment/components/settings/JS_htmlHeader.tmpl +23 -0
  1690. psychopy/experiment/components/settings/JS_setupExp.tmpl +28 -0
  1691. psychopy/experiment/components/settings/__init__.py +2254 -0
  1692. psychopy/experiment/components/settings/classic/settings.png +0 -0
  1693. psychopy/experiment/components/settings/classic/settings@2x.png +0 -0
  1694. psychopy/experiment/components/settings/dark/settings.png +0 -0
  1695. psychopy/experiment/components/settings/dark/settings@2x.png +0 -0
  1696. psychopy/experiment/components/settings/eyetracking.py +108 -0
  1697. psychopy/experiment/components/settings/light/settings.png +0 -0
  1698. psychopy/experiment/components/settings/light/settings@2x.png +0 -0
  1699. psychopy/experiment/components/slider/__init__.py +410 -0
  1700. psychopy/experiment/components/slider/classic/slider.png +0 -0
  1701. psychopy/experiment/components/slider/classic/slider@2x.png +0 -0
  1702. psychopy/experiment/components/slider/dark/slider.png +0 -0
  1703. psychopy/experiment/components/slider/dark/slider@2x.png +0 -0
  1704. psychopy/experiment/components/slider/light/slider.png +0 -0
  1705. psychopy/experiment/components/slider/light/slider@2x.png +0 -0
  1706. psychopy/experiment/components/sound/__init__.py +441 -0
  1707. psychopy/experiment/components/sound/classic/sound.png +0 -0
  1708. psychopy/experiment/components/sound/classic/sound@2x.png +0 -0
  1709. psychopy/experiment/components/sound/dark/sound.png +0 -0
  1710. psychopy/experiment/components/sound/dark/sound@2x.png +0 -0
  1711. psychopy/experiment/components/sound/light/sound.png +0 -0
  1712. psychopy/experiment/components/sound/light/sound@2x.png +0 -0
  1713. psychopy/experiment/components/soundsensor/__init__.py +324 -0
  1714. psychopy/experiment/components/soundsensor/classic/soundsensor.png +0 -0
  1715. psychopy/experiment/components/soundsensor/classic/soundsensor@2x.png +0 -0
  1716. psychopy/experiment/components/soundsensor/dark/soundsensor.png +0 -0
  1717. psychopy/experiment/components/soundsensor/dark/soundsensor@2x.png +0 -0
  1718. psychopy/experiment/components/soundsensor/light/soundsensor.png +0 -0
  1719. psychopy/experiment/components/soundsensor/light/soundsensor@2x.png +0 -0
  1720. psychopy/experiment/components/static/__init__.py +293 -0
  1721. psychopy/experiment/components/static/classic/static.png +0 -0
  1722. psychopy/experiment/components/static/classic/static@2x.png +0 -0
  1723. psychopy/experiment/components/static/dark/static.png +0 -0
  1724. psychopy/experiment/components/static/dark/static@2x.png +0 -0
  1725. psychopy/experiment/components/static/light/static.png +0 -0
  1726. psychopy/experiment/components/static/light/static@2x.png +0 -0
  1727. psychopy/experiment/components/text/__init__.py +190 -0
  1728. psychopy/experiment/components/text/classic/text.png +0 -0
  1729. psychopy/experiment/components/text/classic/text@2x.png +0 -0
  1730. psychopy/experiment/components/text/dark/text.png +0 -0
  1731. psychopy/experiment/components/text/dark/text@2x.png +0 -0
  1732. psychopy/experiment/components/text/light/text.png +0 -0
  1733. psychopy/experiment/components/text/light/text@2x.png +0 -0
  1734. psychopy/experiment/components/textbox/__init__.py +336 -0
  1735. psychopy/experiment/components/textbox/classic/textbox.png +0 -0
  1736. psychopy/experiment/components/textbox/classic/textbox@2x.png +0 -0
  1737. psychopy/experiment/components/textbox/dark/textbox.png +0 -0
  1738. psychopy/experiment/components/textbox/dark/textbox@2x.png +0 -0
  1739. psychopy/experiment/components/textbox/light/textbox.png +0 -0
  1740. psychopy/experiment/components/textbox/light/textbox@2x.png +0 -0
  1741. psychopy/experiment/components/unknown/__init__.py +87 -0
  1742. psychopy/experiment/components/unknown/classic/unknown.png +0 -0
  1743. psychopy/experiment/components/unknown/classic/unknown@2x.png +0 -0
  1744. psychopy/experiment/components/unknown/dark/unknown.png +0 -0
  1745. psychopy/experiment/components/unknown/dark/unknown@2x.png +0 -0
  1746. psychopy/experiment/components/unknown/light/unknown.png +0 -0
  1747. psychopy/experiment/components/unknown/light/unknown@2x.png +0 -0
  1748. psychopy/experiment/components/unknownPlugin/__init__.py +86 -0
  1749. psychopy/experiment/components/unknownPlugin/classic/unknownPlugin.png +0 -0
  1750. psychopy/experiment/components/unknownPlugin/classic/unknownPlugin@2x.png +0 -0
  1751. psychopy/experiment/components/unknownPlugin/dark/unknownPlugin.png +0 -0
  1752. psychopy/experiment/components/unknownPlugin/dark/unknownPlugin@2x.png +0 -0
  1753. psychopy/experiment/components/unknownPlugin/light/unknownPlugin.png +0 -0
  1754. psychopy/experiment/components/unknownPlugin/light/unknownPlugin@2x.png +0 -0
  1755. psychopy/experiment/components/utils.py +0 -0
  1756. psychopy/experiment/components/variable/__init__.py +184 -0
  1757. psychopy/experiment/components/variable/classic/variable.png +0 -0
  1758. psychopy/experiment/components/variable/classic/variable@2x.png +0 -0
  1759. psychopy/experiment/components/variable/dark/variable.png +0 -0
  1760. psychopy/experiment/components/variable/dark/variable@2x.png +0 -0
  1761. psychopy/experiment/components/variable/light/variable.png +0 -0
  1762. psychopy/experiment/components/variable/light/variable@2x.png +0 -0
  1763. psychopy/experiment/devices.py +303 -0
  1764. psychopy/experiment/experiment.xsd +223 -0
  1765. psychopy/experiment/exports.py +390 -0
  1766. psychopy/experiment/flow.py +547 -0
  1767. psychopy/experiment/localization.py +11 -0
  1768. psychopy/experiment/loopTemplate.xltx +0 -0
  1769. psychopy/experiment/loops.py +1051 -0
  1770. psychopy/experiment/monitor.py +74 -0
  1771. psychopy/experiment/params.py +541 -0
  1772. psychopy/experiment/plugins.py +42 -0
  1773. psychopy/experiment/py2js.py +183 -0
  1774. psychopy/experiment/py2js_transpiler.py +613 -0
  1775. psychopy/experiment/questPlusTemplate.xltx +0 -0
  1776. psychopy/experiment/questTemplate.xltx +0 -0
  1777. psychopy/experiment/routines/__init__.py +108 -0
  1778. psychopy/experiment/routines/_base.py +1172 -0
  1779. psychopy/experiment/routines/audioValidator/__init__.py +229 -0
  1780. psychopy/experiment/routines/audioValidator/classic/audio_validator.png +0 -0
  1781. psychopy/experiment/routines/audioValidator/classic/audio_validator@2x.png +0 -0
  1782. psychopy/experiment/routines/audioValidator/dark/audio_validator.png +0 -0
  1783. psychopy/experiment/routines/audioValidator/dark/audio_validator@2x.png +0 -0
  1784. psychopy/experiment/routines/audioValidator/light/audio_validator.png +0 -0
  1785. psychopy/experiment/routines/audioValidator/light/audio_validator@2x.png +0 -0
  1786. psychopy/experiment/routines/counterbalance/__init__.py +320 -0
  1787. psychopy/experiment/routines/counterbalance/classic/counterbalance.png +0 -0
  1788. psychopy/experiment/routines/counterbalance/classic/counterbalance@2x.png +0 -0
  1789. psychopy/experiment/routines/counterbalance/counterbalanceItems.xltx +0 -0
  1790. psychopy/experiment/routines/counterbalance/dark/counterbalance.png +0 -0
  1791. psychopy/experiment/routines/counterbalance/dark/counterbalance@2x.png +0 -0
  1792. psychopy/experiment/routines/counterbalance/light/counterbalance.png +0 -0
  1793. psychopy/experiment/routines/counterbalance/light/counterbalance@2x.png +0 -0
  1794. psychopy/experiment/routines/eyetracker_calibrate/__init__.py +334 -0
  1795. psychopy/experiment/routines/eyetracker_calibrate/classic/eyetracker_calib.png +0 -0
  1796. psychopy/experiment/routines/eyetracker_calibrate/dark/eyetracker_calib.png +0 -0
  1797. psychopy/experiment/routines/eyetracker_calibrate/dark/eyetracker_calib@2x.png +0 -0
  1798. psychopy/experiment/routines/eyetracker_calibrate/light/eyetracker_calib.png +0 -0
  1799. psychopy/experiment/routines/eyetracker_calibrate/light/eyetracker_calib@2x.png +0 -0
  1800. psychopy/experiment/routines/eyetracker_validate/__init__.py +354 -0
  1801. psychopy/experiment/routines/eyetracker_validate/classic/eyetracker_valid.png +0 -0
  1802. psychopy/experiment/routines/eyetracker_validate/dark/eyetracker_valid.png +0 -0
  1803. psychopy/experiment/routines/eyetracker_validate/dark/eyetracker_valid@2x.png +0 -0
  1804. psychopy/experiment/routines/eyetracker_validate/light/eyetracker_valid.png +0 -0
  1805. psychopy/experiment/routines/eyetracker_validate/light/eyetracker_valid@2x.png +0 -0
  1806. psychopy/experiment/routines/pavlovia_survey/__init__.py +236 -0
  1807. psychopy/experiment/routines/pavlovia_survey/classic/survey.png +0 -0
  1808. psychopy/experiment/routines/pavlovia_survey/classic/survey@2x.png +0 -0
  1809. psychopy/experiment/routines/pavlovia_survey/dark/survey.png +0 -0
  1810. psychopy/experiment/routines/pavlovia_survey/dark/survey@2x.png +0 -0
  1811. psychopy/experiment/routines/pavlovia_survey/light/survey.png +0 -0
  1812. psychopy/experiment/routines/pavlovia_survey/light/survey@2x.png +0 -0
  1813. psychopy/experiment/routines/unknown/__init__.py +30 -0
  1814. psychopy/experiment/routines/unknown/classic/eyetracker_record.png +0 -0
  1815. psychopy/experiment/routines/unknown/classic/unknown.png +0 -0
  1816. psychopy/experiment/routines/unknown/dark/eyetracker_record.png +0 -0
  1817. psychopy/experiment/routines/unknown/dark/eyetracker_record@2x.png +0 -0
  1818. psychopy/experiment/routines/unknown/dark/unknown.png +0 -0
  1819. psychopy/experiment/routines/unknown/dark/unknown@2x.png +0 -0
  1820. psychopy/experiment/routines/unknown/light/eyetracker_record.png +0 -0
  1821. psychopy/experiment/routines/unknown/light/eyetracker_record@2x.png +0 -0
  1822. psychopy/experiment/routines/unknown/light/unknown.png +0 -0
  1823. psychopy/experiment/routines/unknown/light/unknown@2x.png +0 -0
  1824. psychopy/experiment/routines/utils.py +0 -0
  1825. psychopy/experiment/routines/visualValidator/__init__.py +395 -0
  1826. psychopy/experiment/routines/visualValidator/classic/visual_validator.png +0 -0
  1827. psychopy/experiment/routines/visualValidator/classic/visual_validator@2x.png +0 -0
  1828. psychopy/experiment/routines/visualValidator/dark/visual_validator.png +0 -0
  1829. psychopy/experiment/routines/visualValidator/dark/visual_validator@2x.png +0 -0
  1830. psychopy/experiment/routines/visualValidator/light/visual_validator.png +0 -0
  1831. psychopy/experiment/routines/visualValidator/light/visual_validator@2x.png +0 -0
  1832. psychopy/experiment/staircaseTemplate.xltx +0 -0
  1833. psychopy/experiment/utils.py +44 -0
  1834. psychopy/filters.py +11 -0
  1835. psychopy/gamma.py +9 -0
  1836. psychopy/gui/__init__.py +47 -0
  1837. psychopy/gui/qtgui.py +882 -0
  1838. psychopy/gui/util.py +74 -0
  1839. psychopy/gui/wxgui.py +420 -0
  1840. psychopy/hardware/__init__.py +178 -0
  1841. psychopy/hardware/base.py +466 -0
  1842. psychopy/hardware/bbtk/__init__.py +24 -0
  1843. psychopy/hardware/brainproducts.py +25 -0
  1844. psychopy/hardware/button.py +238 -0
  1845. psychopy/hardware/buttonbox/__init__.py +122 -0
  1846. psychopy/hardware/camera/__init__.py +3657 -0
  1847. psychopy/hardware/cedrus.py +24 -0
  1848. psychopy/hardware/crs/__init__.py +46 -0
  1849. psychopy/hardware/crs/bits.py +37 -0
  1850. psychopy/hardware/crs/colorcal.py +18 -0
  1851. psychopy/hardware/crs/optical.py +33 -0
  1852. psychopy/hardware/crs/shaders.py +34 -0
  1853. psychopy/hardware/emotiv.py +33 -0
  1854. psychopy/hardware/emulator.py +46 -0
  1855. psychopy/hardware/exceptions.py +86 -0
  1856. psychopy/hardware/eyetracker.py +132 -0
  1857. psychopy/hardware/forp.py +37 -0
  1858. psychopy/hardware/gammasci.py +26 -0
  1859. psychopy/hardware/iolab.py +24 -0
  1860. psychopy/hardware/joystick/__init__.py +1196 -0
  1861. psychopy/hardware/joystick/_base.py +251 -0
  1862. psychopy/hardware/joystick/backend_glfw.py +306 -0
  1863. psychopy/hardware/joystick/backend_pyglet.py +309 -0
  1864. psychopy/hardware/joystick/mappings.py +287 -0
  1865. psychopy/hardware/keyboard.py +975 -0
  1866. psychopy/hardware/knownDevices.json +52 -0
  1867. psychopy/hardware/labhackers.py +27 -0
  1868. psychopy/hardware/labjacks.py +26 -0
  1869. psychopy/hardware/lightsensor.py +789 -0
  1870. psychopy/hardware/listener.py +330 -0
  1871. psychopy/hardware/manager.py +992 -0
  1872. psychopy/hardware/microphone.py +1630 -0
  1873. psychopy/hardware/minolta.py +33 -0
  1874. psychopy/hardware/monitor.py +144 -0
  1875. psychopy/hardware/mouse/__init__.py +885 -0
  1876. psychopy/hardware/photometer/__init__.py +207 -0
  1877. psychopy/hardware/pr.py +33 -0
  1878. psychopy/hardware/qmix.py +32 -0
  1879. psychopy/hardware/serialdevice.py +387 -0
  1880. psychopy/hardware/soundsensor.py +476 -0
  1881. psychopy/hardware/spatial/__init__.py +231 -0
  1882. psychopy/hardware/speaker.py +351 -0
  1883. psychopy/hardware/triggerbox/__init__.py +56 -0
  1884. psychopy/hardware/triggerbox/base.py +170 -0
  1885. psychopy/hardware/triggerbox/parallel.py +362 -0
  1886. psychopy/info.py +719 -0
  1887. psychopy/iohub/__init__.py +50 -0
  1888. psychopy/iohub/client/__init__.py +1481 -0
  1889. psychopy/iohub/client/connect.py +260 -0
  1890. psychopy/iohub/client/eyetracker/__init__.py +4 -0
  1891. psychopy/iohub/client/eyetracker/validation/__init__.py +8 -0
  1892. psychopy/iohub/client/eyetracker/validation/posgrid.py +274 -0
  1893. psychopy/iohub/client/eyetracker/validation/procedure.py +1292 -0
  1894. psychopy/iohub/client/eyetracker/validation/trigger.py +240 -0
  1895. psychopy/iohub/client/keyboard.py +513 -0
  1896. psychopy/iohub/client/wintab.py +378 -0
  1897. psychopy/iohub/constants.py +1176 -0
  1898. psychopy/iohub/datastore/__init__.py +439 -0
  1899. psychopy/iohub/datastore/default_datastore.yaml +8 -0
  1900. psychopy/iohub/datastore/util.py +868 -0
  1901. psychopy/iohub/default_config.yaml +17 -0
  1902. psychopy/iohub/devices/__init__.py +1047 -0
  1903. psychopy/iohub/devices/computer.py +607 -0
  1904. psychopy/iohub/devices/default_device.yaml +15 -0
  1905. psychopy/iohub/devices/deviceConfigValidation.py +519 -0
  1906. psychopy/iohub/devices/display/__init__.py +755 -0
  1907. psychopy/iohub/devices/display/default_display.yaml +118 -0
  1908. psychopy/iohub/devices/display/supported_config_settings.yaml +69 -0
  1909. psychopy/iohub/devices/eventfilters.py +3572 -0
  1910. psychopy/iohub/devices/experiment/__init__.py +316 -0
  1911. psychopy/iohub/devices/experiment/default_experiment.yaml +74 -0
  1912. psychopy/iohub/devices/experiment/supported_config_settings.yaml +26 -0
  1913. psychopy/iohub/devices/eyetracker/__init__.py +474 -0
  1914. psychopy/iohub/devices/eyetracker/calibration/__init__.py +1 -0
  1915. psychopy/iohub/devices/eyetracker/calibration/procedure.py +391 -0
  1916. psychopy/iohub/devices/eyetracker/default_eyetracker.yaml +18 -0
  1917. psychopy/iohub/devices/eyetracker/eye_events.py +1751 -0
  1918. psychopy/iohub/devices/eyetracker/filters/__init__.py +4 -0
  1919. psychopy/iohub/devices/eyetracker/filters/parser.py +913 -0
  1920. psychopy/iohub/devices/eyetracker/hw/__init__.py +4 -0
  1921. psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +29 -0
  1922. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/__init__.py +29 -0
  1923. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/calibration.py +18 -0
  1924. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/eyetracker.py +31 -0
  1925. psychopy/iohub/devices/eyetracker/hw/mouse/__init__.py +8 -0
  1926. psychopy/iohub/devices/eyetracker/hw/mouse/calibration.py +10 -0
  1927. psychopy/iohub/devices/eyetracker/hw/mouse/default_eyetracker.yaml +102 -0
  1928. psychopy/iohub/devices/eyetracker/hw/mouse/eyetracker.py +443 -0
  1929. psychopy/iohub/devices/eyetracker/hw/mouse/supported_config_settings.yaml +110 -0
  1930. psychopy/iohub/devices/eyetracker/hw/pupil_labs/__init__.py +4 -0
  1931. psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/__init__.py +27 -0
  1932. psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/eyetracker.py +18 -0
  1933. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/__init__.py +29 -0
  1934. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/bisector.py +20 -0
  1935. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/constants.py +19 -0
  1936. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/data_parse.py +22 -0
  1937. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/eyetracker.py +18 -0
  1938. psychopy/iohub/devices/eyetracker/hw/pupil_labs/pupil_core/pupil_remote.py +18 -0
  1939. psychopy/iohub/devices/eyetracker/hw/sr_research/__init__.py +4 -0
  1940. psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/__init__.py +28 -0
  1941. psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/calibration.py +22 -0
  1942. psychopy/iohub/devices/eyetracker/hw/sr_research/eyelink/eyetracker.py +38 -0
  1943. psychopy/iohub/devices/eyetracker/hw/tobii/__init__.py +29 -0
  1944. psychopy/iohub/devices/eyetracker/hw/tobii/calibration.py +19 -0
  1945. psychopy/iohub/devices/eyetracker/hw/tobii/eyetracker.py +17 -0
  1946. psychopy/iohub/devices/eyetracker/hw/tobii/tobiiwrapper.py +19 -0
  1947. psychopy/iohub/devices/eyetracker/supported_config_settings.yaml +44 -0
  1948. psychopy/iohub/devices/keyboard/__init__.py +309 -0
  1949. psychopy/iohub/devices/keyboard/darwin.py +431 -0
  1950. psychopy/iohub/devices/keyboard/darwinkey.py +94 -0
  1951. psychopy/iohub/devices/keyboard/default_keyboard.yaml +94 -0
  1952. psychopy/iohub/devices/keyboard/linux2.py +102 -0
  1953. psychopy/iohub/devices/keyboard/supported_config_settings.yaml +39 -0
  1954. psychopy/iohub/devices/keyboard/win32.py +356 -0
  1955. psychopy/iohub/devices/mouse/__init__.py +709 -0
  1956. psychopy/iohub/devices/mouse/darwin.py +414 -0
  1957. psychopy/iohub/devices/mouse/default_mouse.yaml +92 -0
  1958. psychopy/iohub/devices/mouse/linux2.py +172 -0
  1959. psychopy/iohub/devices/mouse/supported_config_settings.yaml +39 -0
  1960. psychopy/iohub/devices/mouse/win32.py +284 -0
  1961. psychopy/iohub/devices/pyXHook.py +619 -0
  1962. psychopy/iohub/devices/serial/__init__.py +779 -0
  1963. psychopy/iohub/devices/serial/default_pstbox.yaml +108 -0
  1964. psychopy/iohub/devices/serial/default_serial.yaml +108 -0
  1965. psychopy/iohub/devices/serial/supported_config_settings.yaml +85 -0
  1966. psychopy/iohub/devices/serial/supported_config_settings_pstbox.yaml +85 -0
  1967. psychopy/iohub/devices/supported_config_settings.yaml +36 -0
  1968. psychopy/iohub/devices/wintab/__init__.py +580 -0
  1969. psychopy/iohub/devices/wintab/default_wintab.yaml +89 -0
  1970. psychopy/iohub/devices/wintab/supported_config_settings.yaml +42 -0
  1971. psychopy/iohub/devices/wintab/win32.py +722 -0
  1972. psychopy/iohub/devices/xlib.py +6795 -0
  1973. psychopy/iohub/errors.py +61 -0
  1974. psychopy/iohub/lazy_import.py +582 -0
  1975. psychopy/iohub/net.py +349 -0
  1976. psychopy/iohub/server.py +1075 -0
  1977. psychopy/iohub/start_iohub_process.py +121 -0
  1978. psychopy/iohub/util/__init__.py +832 -0
  1979. psychopy/iohub/util/visualangle.py +126 -0
  1980. psychopy/layout.py +916 -0
  1981. psychopy/liaison.py +471 -0
  1982. psychopy/locale_setup.py +41 -0
  1983. psychopy/localization/__init__.py +22 -0
  1984. psychopy/localization/_localization.py +155 -0
  1985. psychopy/localization/generateTranslationTemplate.py +257 -0
  1986. psychopy/localization/mappings.txt +170 -0
  1987. psychopy/localization/messages.pot +9950 -0
  1988. psychopy/localization/readme.txt +68 -0
  1989. psychopy/logging.py +415 -0
  1990. psychopy/microphone.py +28 -0
  1991. psychopy/misc.py +40 -0
  1992. psychopy/monitors/MonitorCenter.py +1354 -0
  1993. psychopy/monitors/__init__.py +30 -0
  1994. psychopy/monitors/calibData.py +78 -0
  1995. psychopy/monitors/calibTools.py +1301 -0
  1996. psychopy/monitors/getLumSeries.py +50 -0
  1997. psychopy/monitors/psychopy-icon.svg +395 -0
  1998. psychopy/monitors/psychopy.ico +0 -0
  1999. psychopy/parallel/__init__.py +206 -0
  2000. psychopy/parallel/_dlportio.py +150 -0
  2001. psychopy/parallel/_inpout.py +120 -0
  2002. psychopy/parallel/_linux.py +105 -0
  2003. psychopy/piloting.py +61 -0
  2004. psychopy/platform_specific/__init__.py +46 -0
  2005. psychopy/platform_specific/darwin.py +326 -0
  2006. psychopy/platform_specific/linux.py +77 -0
  2007. psychopy/platform_specific/win32.py +124 -0
  2008. psychopy/plugins/__init__.py +1397 -0
  2009. psychopy/plugins/util.py +104 -0
  2010. psychopy/preferences/Darwin.spec +294 -0
  2011. psychopy/preferences/FreeBSD.spec +294 -0
  2012. psychopy/preferences/Linux.spec +294 -0
  2013. psychopy/preferences/Windows.spec +294 -0
  2014. psychopy/preferences/__init__.py +20 -0
  2015. psychopy/preferences/baseNoArch.spec +290 -0
  2016. psychopy/preferences/devices.py +80 -0
  2017. psychopy/preferences/generateHints.py +93 -0
  2018. psychopy/preferences/generateSpec.py +80 -0
  2019. psychopy/preferences/hints.py +367 -0
  2020. psychopy/preferences/preferences.py +448 -0
  2021. psychopy/projects/__init__.py +11 -0
  2022. psychopy/projects/gitignore.py +45 -0
  2023. psychopy/projects/pavlovia.py +1674 -0
  2024. psychopy/projects/sshkeys.py +107 -0
  2025. psychopy/psychojs.zip +0 -0
  2026. psychopy/pylintrc +69 -0
  2027. psychopy/scripts/__init__.py +0 -0
  2028. psychopy/scripts/psychopy-pkgutil.py +969 -0
  2029. psychopy/scripts/psyexpCompile.py +229 -0
  2030. psychopy/session.py +1651 -0
  2031. psychopy/sound/__init__.py +50 -0
  2032. psychopy/sound/_base.py +399 -0
  2033. psychopy/sound/audioclip.py +1325 -0
  2034. psychopy/sound/audiodevice.py +857 -0
  2035. psychopy/sound/backend_ptb.py +406 -0
  2036. psychopy/sound/backend_pygame.py +323 -0
  2037. psychopy/sound/backend_pyo.py +64 -0
  2038. psychopy/sound/backend_pysound.py +324 -0
  2039. psychopy/sound/backend_sounddevice.py +44 -0
  2040. psychopy/sound/backends/__init__.py +0 -0
  2041. psychopy/sound/exceptions.py +115 -0
  2042. psychopy/sound/microphone.py +357 -0
  2043. psychopy/sound/sound.py +58 -0
  2044. psychopy/sound/transcribe.py +1321 -0
  2045. psychopy/tests/README.md +344 -0
  2046. psychopy/tests/__init__.py +47 -0
  2047. psychopy/tests/data/Electronic_Chime-KevanGC-495939803.wav +0 -0
  2048. psychopy/tests/data/TestCircle_w128h128_bottom_left.png +0 -0
  2049. psychopy/tests/data/TestCircle_w128h128_bottom_right.png +0 -0
  2050. psychopy/tests/data/TestCircle_w128h128_center_center.png +0 -0
  2051. psychopy/tests/data/TestCircle_w128h128_top_left.png +0 -0
  2052. psychopy/tests/data/TestCircle_w128h128_top_right.png +0 -0
  2053. psychopy/tests/data/TestCircle_w128h64_bottom_left.png +0 -0
  2054. psychopy/tests/data/TestCircle_w128h64_bottom_right.png +0 -0
  2055. psychopy/tests/data/TestCircle_w128h64_center_center.png +0 -0
  2056. psychopy/tests/data/TestCircle_w128h64_top_left.png +0 -0
  2057. psychopy/tests/data/TestCircle_w128h64_top_right.png +0 -0
  2058. psychopy/tests/data/TestCircle_w64h128_bottom_left.png +0 -0
  2059. psychopy/tests/data/TestCircle_w64h128_bottom_right.png +0 -0
  2060. psychopy/tests/data/TestCircle_w64h128_center_center.png +0 -0
  2061. psychopy/tests/data/TestCircle_w64h128_top_left.png +0 -0
  2062. psychopy/tests/data/TestCircle_w64h128_top_right.png +0 -0
  2063. psychopy/tests/data/TestCircle_w64h64_bottom_left.png +0 -0
  2064. psychopy/tests/data/TestCircle_w64h64_bottom_right.png +0 -0
  2065. psychopy/tests/data/TestCircle_w64h64_center_center.png +0 -0
  2066. psychopy/tests/data/TestCircle_w64h64_top_left.png +0 -0
  2067. psychopy/tests/data/TestCircle_w64h64_top_right.png +0 -0
  2068. psychopy/tests/data/TestForm_scrolling_nq10_s0.5.png +0 -0
  2069. psychopy/tests/data/TestForm_scrolling_nq10_s0.png +0 -0
  2070. psychopy/tests/data/TestForm_scrolling_nq10_s1.png +0 -0
  2071. psychopy/tests/data/TestForm_scrolling_nq1_s0.5.png +0 -0
  2072. psychopy/tests/data/TestForm_scrolling_nq1_s0.png +0 -0
  2073. psychopy/tests/data/TestForm_scrolling_nq1_s1.png +0 -0
  2074. psychopy/tests/data/TestForm_scrolling_nq3_s0.5.png +0 -0
  2075. psychopy/tests/data/TestForm_scrolling_nq3_s0.png +0 -0
  2076. psychopy/tests/data/TestForm_scrolling_nq3_s1.png +0 -0
  2077. psychopy/tests/data/TestImage_w128h128_bottom_left.png +0 -0
  2078. psychopy/tests/data/TestImage_w128h128_bottom_right.png +0 -0
  2079. psychopy/tests/data/TestImage_w128h128_center_center.png +0 -0
  2080. psychopy/tests/data/TestImage_w128h128_top_left.png +0 -0
  2081. psychopy/tests/data/TestImage_w128h128_top_right.png +0 -0
  2082. psychopy/tests/data/TestImage_w128h64_bottom_left.png +0 -0
  2083. psychopy/tests/data/TestImage_w128h64_bottom_right.png +0 -0
  2084. psychopy/tests/data/TestImage_w128h64_center_center.png +0 -0
  2085. psychopy/tests/data/TestImage_w128h64_top_left.png +0 -0
  2086. psychopy/tests/data/TestImage_w128h64_top_right.png +0 -0
  2087. psychopy/tests/data/TestImage_w64h128_bottom_left.png +0 -0
  2088. psychopy/tests/data/TestImage_w64h128_bottom_right.png +0 -0
  2089. psychopy/tests/data/TestImage_w64h128_center_center.png +0 -0
  2090. psychopy/tests/data/TestImage_w64h128_top_left.png +0 -0
  2091. psychopy/tests/data/TestImage_w64h128_top_right.png +0 -0
  2092. psychopy/tests/data/TestImage_w64h64_bottom_left.png +0 -0
  2093. psychopy/tests/data/TestImage_w64h64_bottom_right.png +0 -0
  2094. psychopy/tests/data/TestImage_w64h64_center_center.png +0 -0
  2095. psychopy/tests/data/TestImage_w64h64_top_left.png +0 -0
  2096. psychopy/tests/data/TestImage_w64h64_top_right.png +0 -0
  2097. psychopy/tests/data/TestProgress_testValue_horizontal_center center_0.3.png +0 -0
  2098. psychopy/tests/data/TestProgress_testValue_horizontal_center center_0.6.png +0 -0
  2099. psychopy/tests/data/TestProgress_testValue_horizontal_left center_0.3.png +0 -0
  2100. psychopy/tests/data/TestProgress_testValue_horizontal_left center_0.6.png +0 -0
  2101. psychopy/tests/data/TestProgress_testValue_horizontal_right center_0.3.png +0 -0
  2102. psychopy/tests/data/TestProgress_testValue_horizontal_right center_0.6.png +0 -0
  2103. psychopy/tests/data/TestProgress_testValue_minmax_0.png +0 -0
  2104. psychopy/tests/data/TestProgress_testValue_minmax_1.png +0 -0
  2105. psychopy/tests/data/TestProgress_testValue_vertical_bottom center_0.3.png +0 -0
  2106. psychopy/tests/data/TestProgress_testValue_vertical_bottom center_0.6.png +0 -0
  2107. psychopy/tests/data/TestProgress_testValue_vertical_center center_0.3.png +0 -0
  2108. psychopy/tests/data/TestProgress_testValue_vertical_center center_0.6.png +0 -0
  2109. psychopy/tests/data/TestProgress_testValue_vertical_top center_0.3.png +0 -0
  2110. psychopy/tests/data/TestProgress_testValue_vertical_top center_0.6.png +0 -0
  2111. psychopy/tests/data/TestProgress_w128h128_bottom_left.png +0 -0
  2112. psychopy/tests/data/TestProgress_w128h128_bottom_right.png +0 -0
  2113. psychopy/tests/data/TestProgress_w128h128_center_center.png +0 -0
  2114. psychopy/tests/data/TestProgress_w128h128_top_left.png +0 -0
  2115. psychopy/tests/data/TestProgress_w128h128_top_right.png +0 -0
  2116. psychopy/tests/data/TestProgress_w128h64_bottom_left.png +0 -0
  2117. psychopy/tests/data/TestProgress_w128h64_bottom_right.png +0 -0
  2118. psychopy/tests/data/TestProgress_w128h64_center_center.png +0 -0
  2119. psychopy/tests/data/TestProgress_w128h64_top_left.png +0 -0
  2120. psychopy/tests/data/TestProgress_w128h64_top_right.png +0 -0
  2121. psychopy/tests/data/TestProgress_w64h128_bottom_left.png +0 -0
  2122. psychopy/tests/data/TestProgress_w64h128_bottom_right.png +0 -0
  2123. psychopy/tests/data/TestProgress_w64h128_center_center.png +0 -0
  2124. psychopy/tests/data/TestProgress_w64h128_top_left.png +0 -0
  2125. psychopy/tests/data/TestProgress_w64h128_top_right.png +0 -0
  2126. psychopy/tests/data/TestProgress_w64h64_bottom_left.png +0 -0
  2127. psychopy/tests/data/TestProgress_w64h64_bottom_right.png +0 -0
  2128. psychopy/tests/data/TestProgress_w64h64_center_center.png +0 -0
  2129. psychopy/tests/data/TestProgress_w64h64_top_left.png +0 -0
  2130. psychopy/tests/data/TestProgress_w64h64_top_right.png +0 -0
  2131. psychopy/tests/data/TestROI_w128h128_bottom_left.png +0 -0
  2132. psychopy/tests/data/TestROI_w128h128_bottom_right.png +0 -0
  2133. psychopy/tests/data/TestROI_w128h128_center_center.png +0 -0
  2134. psychopy/tests/data/TestROI_w128h128_top_left.png +0 -0
  2135. psychopy/tests/data/TestROI_w128h128_top_right.png +0 -0
  2136. psychopy/tests/data/TestROI_w128h64_bottom_left.png +0 -0
  2137. psychopy/tests/data/TestROI_w128h64_bottom_right.png +0 -0
  2138. psychopy/tests/data/TestROI_w128h64_center_center.png +0 -0
  2139. psychopy/tests/data/TestROI_w128h64_top_left.png +0 -0
  2140. psychopy/tests/data/TestROI_w128h64_top_right.png +0 -0
  2141. psychopy/tests/data/TestROI_w64h128_bottom_left.png +0 -0
  2142. psychopy/tests/data/TestROI_w64h128_bottom_right.png +0 -0
  2143. psychopy/tests/data/TestROI_w64h128_center_center.png +0 -0
  2144. psychopy/tests/data/TestROI_w64h128_top_left.png +0 -0
  2145. psychopy/tests/data/TestROI_w64h128_top_right.png +0 -0
  2146. psychopy/tests/data/TestROI_w64h64_bottom_left.png +0 -0
  2147. psychopy/tests/data/TestROI_w64h64_bottom_right.png +0 -0
  2148. psychopy/tests/data/TestROI_w64h64_center_center.png +0 -0
  2149. psychopy/tests/data/TestROI_w64h64_top_left.png +0 -0
  2150. psychopy/tests/data/TestROI_w64h64_top_right.png +0 -0
  2151. psychopy/tests/data/TestSession_exp1.png +0 -0
  2152. psychopy/tests/data/TestSession_exp2.png +0 -0
  2153. psychopy/tests/data/TestShape_w128h128_bottom_left.png +0 -0
  2154. psychopy/tests/data/TestShape_w128h128_bottom_right.png +0 -0
  2155. psychopy/tests/data/TestShape_w128h128_center_center.png +0 -0
  2156. psychopy/tests/data/TestShape_w128h128_top_left.png +0 -0
  2157. psychopy/tests/data/TestShape_w128h128_top_right.png +0 -0
  2158. psychopy/tests/data/TestShape_w128h64_bottom_left.png +0 -0
  2159. psychopy/tests/data/TestShape_w128h64_bottom_right.png +0 -0
  2160. psychopy/tests/data/TestShape_w128h64_center_center.png +0 -0
  2161. psychopy/tests/data/TestShape_w128h64_top_left.png +0 -0
  2162. psychopy/tests/data/TestShape_w128h64_top_right.png +0 -0
  2163. psychopy/tests/data/TestShape_w64h128_bottom_left.png +0 -0
  2164. psychopy/tests/data/TestShape_w64h128_bottom_right.png +0 -0
  2165. psychopy/tests/data/TestShape_w64h128_center_center.png +0 -0
  2166. psychopy/tests/data/TestShape_w64h128_top_left.png +0 -0
  2167. psychopy/tests/data/TestShape_w64h128_top_right.png +0 -0
  2168. psychopy/tests/data/TestShape_w64h64_bottom_left.png +0 -0
  2169. psychopy/tests/data/TestShape_w64h64_bottom_right.png +0 -0
  2170. psychopy/tests/data/TestShape_w64h64_center_center.png +0 -0
  2171. psychopy/tests/data/TestShape_w64h64_top_left.png +0 -0
  2172. psychopy/tests/data/TestShape_w64h64_top_right.png +0 -0
  2173. psychopy/tests/data/TestTarget_w128h128_bottom_left.png +0 -0
  2174. psychopy/tests/data/TestTarget_w128h128_bottom_right.png +0 -0
  2175. psychopy/tests/data/TestTarget_w128h128_center_center.png +0 -0
  2176. psychopy/tests/data/TestTarget_w128h128_top_left.png +0 -0
  2177. psychopy/tests/data/TestTarget_w128h128_top_right.png +0 -0
  2178. psychopy/tests/data/TestTarget_w128h64_bottom_left.png +0 -0
  2179. psychopy/tests/data/TestTarget_w128h64_bottom_right.png +0 -0
  2180. psychopy/tests/data/TestTarget_w128h64_center_center.png +0 -0
  2181. psychopy/tests/data/TestTarget_w128h64_top_left.png +0 -0
  2182. psychopy/tests/data/TestTarget_w128h64_top_right.png +0 -0
  2183. psychopy/tests/data/TestTarget_w64h128_bottom_left.png +0 -0
  2184. psychopy/tests/data/TestTarget_w64h128_bottom_right.png +0 -0
  2185. psychopy/tests/data/TestTarget_w64h128_center_center.png +0 -0
  2186. psychopy/tests/data/TestTarget_w64h128_top_left.png +0 -0
  2187. psychopy/tests/data/TestTarget_w64h128_top_right.png +0 -0
  2188. psychopy/tests/data/TestTarget_w64h64_bottom_left.png +0 -0
  2189. psychopy/tests/data/TestTarget_w64h64_bottom_right.png +0 -0
  2190. psychopy/tests/data/TestTarget_w64h64_center_center.png +0 -0
  2191. psychopy/tests/data/TestTarget_w64h64_top_left.png +0 -0
  2192. psychopy/tests/data/TestTarget_w64h64_top_right.png +0 -0
  2193. psychopy/tests/data/TestTextbox_testLetterSpacing_0p6.png +0 -0
  2194. psychopy/tests/data/TestTextbox_testLetterSpacing_0p8.png +0 -0
  2195. psychopy/tests/data/TestTextbox_testLetterSpacing_1.png +0 -0
  2196. psychopy/tests/data/TestTextbox_testLetterSpacing_1p2.png +0 -0
  2197. psychopy/tests/data/TestTextbox_testLetterSpacing_1p4.png +0 -0
  2198. psychopy/tests/data/TestTextbox_testLetterSpacing_1p6.png +0 -0
  2199. psychopy/tests/data/TestTextbox_testLetterSpacing_1p8.png +0 -0
  2200. psychopy/tests/data/TestTextbox_testLetterSpacing_2p0.png +0 -0
  2201. psychopy/tests/data/TestTextbox_testLetterSpacing_None.png +0 -0
  2202. psychopy/tests/data/TestUnknownPluginComponent_load_resave.psyexp +135 -0
  2203. psychopy/tests/data/Test_textbox/test_ori_0_bottom right.png +0 -0
  2204. psychopy/tests/data/Test_textbox/test_ori_0_center.png +0 -0
  2205. psychopy/tests/data/Test_textbox/test_ori_0_top left.png +0 -0
  2206. psychopy/tests/data/Test_textbox/test_ori_120_bottom right.png +0 -0
  2207. psychopy/tests/data/Test_textbox/test_ori_120_center.png +0 -0
  2208. psychopy/tests/data/Test_textbox/test_ori_120_top left.png +0 -0
  2209. psychopy/tests/data/Test_textbox/test_ori_180_bottom right.png +0 -0
  2210. psychopy/tests/data/Test_textbox/test_ori_180_center.png +0 -0
  2211. psychopy/tests/data/Test_textbox/test_ori_180_top left.png +0 -0
  2212. psychopy/tests/data/Test_textbox/test_ori_240_bottom right.png +0 -0
  2213. psychopy/tests/data/Test_textbox/test_ori_240_center.png +0 -0
  2214. psychopy/tests/data/Test_textbox/test_ori_240_top left.png +0 -0
  2215. psychopy/tests/data/Test_textbox_testSpeechpoint_-3ovr8_0ovr8.png +0 -0
  2216. psychopy/tests/data/Test_textbox_testSpeechpoint_0ovr8_-3ovr8.png +0 -0
  2217. psychopy/tests/data/Test_textbox_testSpeechpoint_0ovr8_3ovr8.png +0 -0
  2218. psychopy/tests/data/Test_textbox_testSpeechpoint_3ovr8_0ovr8.png +0 -0
  2219. psychopy/tests/data/Test_textbox_w128h128_bottom_left.png +0 -0
  2220. psychopy/tests/data/Test_textbox_w128h128_bottom_right.png +0 -0
  2221. psychopy/tests/data/Test_textbox_w128h128_center_center.png +0 -0
  2222. psychopy/tests/data/Test_textbox_w128h128_top_left.png +0 -0
  2223. psychopy/tests/data/Test_textbox_w128h128_top_right.png +0 -0
  2224. psychopy/tests/data/Test_textbox_w128h64_bottom_left.png +0 -0
  2225. psychopy/tests/data/Test_textbox_w128h64_bottom_right.png +0 -0
  2226. psychopy/tests/data/Test_textbox_w128h64_center_center.png +0 -0
  2227. psychopy/tests/data/Test_textbox_w128h64_top_left.png +0 -0
  2228. psychopy/tests/data/Test_textbox_w128h64_top_right.png +0 -0
  2229. psychopy/tests/data/Test_textbox_w64h128_bottom_left.png +0 -0
  2230. psychopy/tests/data/Test_textbox_w64h128_bottom_right.png +0 -0
  2231. psychopy/tests/data/Test_textbox_w64h128_center_center.png +0 -0
  2232. psychopy/tests/data/Test_textbox_w64h128_top_left.png +0 -0
  2233. psychopy/tests/data/Test_textbox_w64h128_top_right.png +0 -0
  2234. psychopy/tests/data/Test_textbox_w64h64_bottom_left.png +0 -0
  2235. psychopy/tests/data/Test_textbox_w64h64_bottom_right.png +0 -0
  2236. psychopy/tests/data/Test_textbox_w64h64_center_center.png +0 -0
  2237. psychopy/tests/data/Test_textbox_w64h64_top_left.png +0 -0
  2238. psychopy/tests/data/Test_textbox_w64h64_top_right.png +0 -0
  2239. psychopy/tests/data/Test_uax14_textbox_w128h128_bottom_left.png +0 -0
  2240. psychopy/tests/data/Test_uax14_textbox_w128h128_bottom_right.png +0 -0
  2241. psychopy/tests/data/Test_uax14_textbox_w128h128_center_center.png +0 -0
  2242. psychopy/tests/data/Test_uax14_textbox_w128h128_top_left.png +0 -0
  2243. psychopy/tests/data/Test_uax14_textbox_w128h128_top_right.png +0 -0
  2244. psychopy/tests/data/Test_uax14_textbox_w128h64_bottom_left.png +0 -0
  2245. psychopy/tests/data/Test_uax14_textbox_w128h64_bottom_right.png +0 -0
  2246. psychopy/tests/data/Test_uax14_textbox_w128h64_center_center.png +0 -0
  2247. psychopy/tests/data/Test_uax14_textbox_w128h64_top_left.png +0 -0
  2248. psychopy/tests/data/Test_uax14_textbox_w128h64_top_right.png +0 -0
  2249. psychopy/tests/data/Test_uax14_textbox_w64h128_bottom_left.png +0 -0
  2250. psychopy/tests/data/Test_uax14_textbox_w64h128_bottom_right.png +0 -0
  2251. psychopy/tests/data/Test_uax14_textbox_w64h128_center_center.png +0 -0
  2252. psychopy/tests/data/Test_uax14_textbox_w64h128_top_left.png +0 -0
  2253. psychopy/tests/data/Test_uax14_textbox_w64h128_top_right.png +0 -0
  2254. psychopy/tests/data/Test_uax14_textbox_w64h64_bottom_left.png +0 -0
  2255. psychopy/tests/data/Test_uax14_textbox_w64h64_bottom_right.png +0 -0
  2256. psychopy/tests/data/Test_uax14_textbox_w64h64_center_center.png +0 -0
  2257. psychopy/tests/data/Test_uax14_textbox_w64h64_top_left.png +0 -0
  2258. psychopy/tests/data/Test_uax14_textbox_w64h64_top_right.png +0 -0
  2259. psychopy/tests/data/TextComponent_disabled.psyexp +63 -0
  2260. psychopy/tests/data/TextComponent_not_disabled.psyexp +63 -0
  2261. psychopy/tests/data/aperture1_deg.png +0 -0
  2262. psychopy/tests/data/aperture1_degFlat.png +0 -0
  2263. psychopy/tests/data/aperture1_degFlatPos.png +0 -0
  2264. psychopy/tests/data/aperture1_norm.png +0 -0
  2265. psychopy/tests/data/aperture1_normHexbackground.png +0 -0
  2266. psychopy/tests/data/aperture1_normNoShade.png +0 -0
  2267. psychopy/tests/data/aperture1_pix.png +0 -0
  2268. psychopy/tests/data/aperture1_stencil.png +0 -0
  2269. psychopy/tests/data/aperture2_deg.png +0 -0
  2270. psychopy/tests/data/aperture2_degFlat.png +0 -0
  2271. psychopy/tests/data/aperture2_degFlatPos.png +0 -0
  2272. psychopy/tests/data/aperture2_norm.png +0 -0
  2273. psychopy/tests/data/aperture2_normHexbackground.png +0 -0
  2274. psychopy/tests/data/aperture2_normNoShade.png +0 -0
  2275. psychopy/tests/data/aperture2_pix.png +0 -0
  2276. psychopy/tests/data/aperture2_stencil.png +0 -0
  2277. psychopy/tests/data/beatandrcos_cm.png +0 -0
  2278. psychopy/tests/data/beatandrcos_deg.png +0 -0
  2279. psychopy/tests/data/beatandrcos_degFlat.png +0 -0
  2280. psychopy/tests/data/beatandrcos_degFlatPos.png +0 -0
  2281. psychopy/tests/data/beatandrcos_height.png +0 -0
  2282. psychopy/tests/data/beatandrcos_norm.png +0 -0
  2283. psychopy/tests/data/beatandrcos_normAddBlend.png +0 -0
  2284. psychopy/tests/data/beatandrcos_normHexbackground.png +0 -0
  2285. psychopy/tests/data/beatandrcos_pix.png +0 -0
  2286. psychopy/tests/data/beatandrcos_stencil.png +0 -0
  2287. psychopy/tests/data/blend_add_cm.png +0 -0
  2288. psychopy/tests/data/blend_add_deg.png +0 -0
  2289. psychopy/tests/data/blend_add_degFlat.png +0 -0
  2290. psychopy/tests/data/blend_add_degFlatPos.png +0 -0
  2291. psychopy/tests/data/blend_add_height.png +0 -0
  2292. psychopy/tests/data/blend_add_norm.png +0 -0
  2293. psychopy/tests/data/blend_add_normAddBlend.png +0 -0
  2294. psychopy/tests/data/blend_add_normHexbackground.png +0 -0
  2295. psychopy/tests/data/blend_add_normNoShade.png +0 -0
  2296. psychopy/tests/data/blend_add_pix.png +0 -0
  2297. psychopy/tests/data/blend_add_stencil.png +0 -0
  2298. psychopy/tests/data/broken2020_2_5_resources/broken_resources.psyexp +84 -0
  2299. psychopy/tests/data/broken2020_2_5_resources/psychopy72.png +0 -0
  2300. psychopy/tests/data/bufferimg_gabor_cm.png +0 -0
  2301. psychopy/tests/data/bufferimg_gabor_deg.png +0 -0
  2302. psychopy/tests/data/bufferimg_gabor_degFlat.png +0 -0
  2303. psychopy/tests/data/bufferimg_gabor_degFlatPos.png +0 -0
  2304. psychopy/tests/data/bufferimg_gabor_height.png +0 -0
  2305. psychopy/tests/data/bufferimg_gabor_norm.png +0 -0
  2306. psychopy/tests/data/bufferimg_gabor_normAddBlend.png +0 -0
  2307. psychopy/tests/data/bufferimg_gabor_normHexbackground.png +0 -0
  2308. psychopy/tests/data/bufferimg_gabor_normNoShade.png +0 -0
  2309. psychopy/tests/data/bufferimg_gabor_pix.png +0 -0
  2310. psychopy/tests/data/bufferimg_gabor_stencil.png +0 -0
  2311. psychopy/tests/data/circleHex_cm.png +0 -0
  2312. psychopy/tests/data/circleHex_deg.png +0 -0
  2313. psychopy/tests/data/circleHex_degFlat.png +0 -0
  2314. psychopy/tests/data/circleHex_degFlatPos.png +0 -0
  2315. psychopy/tests/data/circleHex_height.png +0 -0
  2316. psychopy/tests/data/circleHex_norm.png +0 -0
  2317. psychopy/tests/data/circleHex_normAddBlend.png +0 -0
  2318. psychopy/tests/data/circleHex_normHexbackground.png +0 -0
  2319. psychopy/tests/data/circleHex_normNoShade.png +0 -0
  2320. psychopy/tests/data/circleHex_pix.png +0 -0
  2321. psychopy/tests/data/circleHex_stencil.png +0 -0
  2322. psychopy/tests/data/corrFullRandom.csv +16 -0
  2323. psychopy/tests/data/corrFullRandom.tsv +6 -0
  2324. psychopy/tests/data/corrFullRandomTH2.csv +16 -0
  2325. psychopy/tests/data/corrMultiKeyExpWide.csv +3 -0
  2326. psychopy/tests/data/corrMultiKeyTrials.csv +8 -0
  2327. psychopy/tests/data/corrMultiKeyTrials.xlsx +0 -0
  2328. psychopy/tests/data/corrRandom.csv +16 -0
  2329. psychopy/tests/data/corrRandom.tsv +6 -0
  2330. psychopy/tests/data/corrRandomTH2.csv +16 -0
  2331. psychopy/tests/data/corrXlsx.xlsx +0 -0
  2332. psychopy/tests/data/correctScript/js/correctCodeComponent.js +181 -0
  2333. psychopy/tests/data/correctScript/js/correctImageComponent.js +204 -0
  2334. psychopy/tests/data/correctScript/js/correctKeyboardComponent.js +225 -0
  2335. psychopy/tests/data/correctScript/js/correctMouseComponent.js +221 -0
  2336. psychopy/tests/data/correctScript/js/correctMovieComponent.js +210 -0
  2337. psychopy/tests/data/correctScript/js/correctPolygonComponent.js +205 -0
  2338. psychopy/tests/data/correctScript/js/correctSliderComponent.js +209 -0
  2339. psychopy/tests/data/correctScript/js/correctSoundComponent.js +205 -0
  2340. psychopy/tests/data/correctScript/js/correctTextComponent.js +207 -0
  2341. psychopy/tests/data/correctScript/python/correctApertureComponent.py +157 -0
  2342. psychopy/tests/data/correctScript/python/correctCodeComponent.py +135 -0
  2343. psychopy/tests/data/correctScript/python/correctDotsComponent.py +161 -0
  2344. psychopy/tests/data/correctScript/python/correctEnvGratingComponent.py +167 -0
  2345. psychopy/tests/data/correctScript/python/correctFormComponent.py +150 -0
  2346. psychopy/tests/data/correctScript/python/correctGratingComponent.py +158 -0
  2347. psychopy/tests/data/correctScript/python/correctImageComponent.py +160 -0
  2348. psychopy/tests/data/correctScript/python/correctJoyButtonsComponent.py +204 -0
  2349. psychopy/tests/data/correctScript/python/correctJoystickComponent.py +214 -0
  2350. psychopy/tests/data/correctScript/python/correctKeyboardComponent.py +169 -0
  2351. psychopy/tests/data/correctScript/python/correctMicrophoneComponent.py +163 -0
  2352. psychopy/tests/data/correctScript/python/correctMouseComponent.py +174 -0
  2353. psychopy/tests/data/correctScript/python/correctMovieComponent.py +160 -0
  2354. psychopy/tests/data/correctScript/python/correctNoiseStimComponent.py +169 -0
  2355. psychopy/tests/data/correctScript/python/correctParallelOutComponent.py +156 -0
  2356. psychopy/tests/data/correctScript/python/correctPatchComponent.py +159 -0
  2357. psychopy/tests/data/correctScript/python/correctPolygonComponent.py +159 -0
  2358. psychopy/tests/data/correctScript/python/correctQmixPumpComponent.py +174 -0
  2359. psychopy/tests/data/correctScript/python/correctRatingScaleComponent.py +152 -0
  2360. psychopy/tests/data/correctScript/python/correctSliderComponent.py +159 -0
  2361. psychopy/tests/data/correctScript/python/correctSoundComponent.py +157 -0
  2362. psychopy/tests/data/correctScript/python/correctStaticComponent.py +176 -0
  2363. psychopy/tests/data/correctScript/python/correctTextComponent.py +159 -0
  2364. psychopy/tests/data/correctScript/python/correctVariableComponent.py +139 -0
  2365. psychopy/tests/data/correctScript/python/correctcedrusButtonBoxComponent.py +199 -0
  2366. psychopy/tests/data/correctScript/python/correctioLabsButtonBoxComponent.py +187 -0
  2367. psychopy/tests/data/dataTest.xlsx +0 -0
  2368. psychopy/tests/data/dots_cm.png +0 -0
  2369. psychopy/tests/data/dots_deg.png +0 -0
  2370. psychopy/tests/data/dots_degFlat.png +0 -0
  2371. psychopy/tests/data/dots_degFlatPos.png +0 -0
  2372. psychopy/tests/data/dots_height.png +0 -0
  2373. psychopy/tests/data/dots_norm.png +0 -0
  2374. psychopy/tests/data/dots_normAddBlend.png +0 -0
  2375. psychopy/tests/data/dots_normHexbackground.png +0 -0
  2376. psychopy/tests/data/dots_normNoShade.png +0 -0
  2377. psychopy/tests/data/dots_pix.png +0 -0
  2378. psychopy/tests/data/dots_stencil.png +0 -0
  2379. psychopy/tests/data/duplicateHeaders.csv +2 -0
  2380. psychopy/tests/data/elarray1_cm.png +0 -0
  2381. psychopy/tests/data/elarray1_deg.png +0 -0
  2382. psychopy/tests/data/elarray1_degFlat.png +0 -0
  2383. psychopy/tests/data/elarray1_degFlatPos.png +0 -0
  2384. psychopy/tests/data/elarray1_height.png +0 -0
  2385. psychopy/tests/data/elarray1_norm.png +0 -0
  2386. psychopy/tests/data/elarray1_normAddBlend.png +0 -0
  2387. psychopy/tests/data/elarray1_normHexbackground.png +0 -0
  2388. psychopy/tests/data/elarray1_pix.png +0 -0
  2389. psychopy/tests/data/elarray1_stencil.png +0 -0
  2390. psychopy/tests/data/envelopeandrcos_cm.png +0 -0
  2391. psychopy/tests/data/envelopeandrcos_deg.png +0 -0
  2392. psychopy/tests/data/envelopeandrcos_degFlat.png +0 -0
  2393. psychopy/tests/data/envelopeandrcos_degFlatPos.png +0 -0
  2394. psychopy/tests/data/envelopeandrcos_height.png +0 -0
  2395. psychopy/tests/data/envelopeandrcos_norm.png +0 -0
  2396. psychopy/tests/data/envelopeandrcos_normAddBlend.png +0 -0
  2397. psychopy/tests/data/envelopeandrcos_normHexbackground.png +0 -0
  2398. psychopy/tests/data/envelopeandrcos_pix.png +0 -0
  2399. psychopy/tests/data/envelopeandrcos_stencil.png +0 -0
  2400. psychopy/tests/data/envelopepowerandrcos_cm.png +0 -0
  2401. psychopy/tests/data/envelopepowerandrcos_deg.png +0 -0
  2402. psychopy/tests/data/envelopepowerandrcos_degFlat.png +0 -0
  2403. psychopy/tests/data/envelopepowerandrcos_degFlatPos.png +0 -0
  2404. psychopy/tests/data/envelopepowerandrcos_height.png +0 -0
  2405. psychopy/tests/data/envelopepowerandrcos_norm.png +0 -0
  2406. psychopy/tests/data/envelopepowerandrcos_normAddBlend.png +0 -0
  2407. psychopy/tests/data/envelopepowerandrcos_normHexbackground.png +0 -0
  2408. psychopy/tests/data/envelopepowerandrcos_pix.png +0 -0
  2409. psychopy/tests/data/envelopepowerandrcos_stencil.png +0 -0
  2410. psychopy/tests/data/filltext.png +0 -0
  2411. psychopy/tests/data/form_font_demographics.png +0 -0
  2412. psychopy/tests/data/form_font_demographics.xlsx +0 -0
  2413. psychopy/tests/data/form_font_languages.png +0 -0
  2414. psychopy/tests/data/form_font_languages.xlsx +0 -0
  2415. psychopy/tests/data/form_font_nonstandard.png +0 -0
  2416. psychopy/tests/data/futureParams.psyexp +150 -0
  2417. psychopy/tests/data/gabor1_cm.png +0 -0
  2418. psychopy/tests/data/gabor1_deg.png +0 -0
  2419. psychopy/tests/data/gabor1_degFlat.png +0 -0
  2420. psychopy/tests/data/gabor1_degFlatPos.png +0 -0
  2421. psychopy/tests/data/gabor1_height.png +0 -0
  2422. psychopy/tests/data/gabor1_norm.png +0 -0
  2423. psychopy/tests/data/gabor1_normAddBlend.png +0 -0
  2424. psychopy/tests/data/gabor1_normHexbackground.png +0 -0
  2425. psychopy/tests/data/gabor1_normNoShade.png +0 -0
  2426. psychopy/tests/data/gabor1_pix.png +0 -0
  2427. psychopy/tests/data/gabor1_stencil.png +0 -0
  2428. psychopy/tests/data/gabor2_cm.png +0 -0
  2429. psychopy/tests/data/gabor2_deg.png +0 -0
  2430. psychopy/tests/data/gabor2_degFlat.png +0 -0
  2431. psychopy/tests/data/gabor2_degFlatPos.png +0 -0
  2432. psychopy/tests/data/gabor2_height.png +0 -0
  2433. psychopy/tests/data/gabor2_norm.png +0 -0
  2434. psychopy/tests/data/gabor2_normAddBlend.png +0 -0
  2435. psychopy/tests/data/gabor2_normHexbackground.png +0 -0
  2436. psychopy/tests/data/gabor2_normNoShade.png +0 -0
  2437. psychopy/tests/data/gabor2_pix.png +0 -0
  2438. psychopy/tests/data/gabor2_stencil.png +0 -0
  2439. psychopy/tests/data/ghost_stroop.psyexp +131 -0
  2440. psychopy/tests/data/ghost_trialTypes.xlsx +0 -0
  2441. psychopy/tests/data/green_48000.flac.dist +0 -0
  2442. psychopy/tests/data/greyscale.jpg +0 -0
  2443. psychopy/tests/data/greyscale2.png +0 -0
  2444. psychopy/tests/data/greyscale2_cm.png +0 -0
  2445. psychopy/tests/data/greyscale2_deg.png +0 -0
  2446. psychopy/tests/data/greyscale2_degFlat.png +0 -0
  2447. psychopy/tests/data/greyscale2_degFlatPos.png +0 -0
  2448. psychopy/tests/data/greyscale2_height.png +0 -0
  2449. psychopy/tests/data/greyscale2_norm.png +0 -0
  2450. psychopy/tests/data/greyscale2_normAddBlend.png +0 -0
  2451. psychopy/tests/data/greyscale2_normHexbackground.png +0 -0
  2452. psychopy/tests/data/greyscale2_normNoShade.png +0 -0
  2453. psychopy/tests/data/greyscale2_pix.png +0 -0
  2454. psychopy/tests/data/greyscale2_stencil.png +0 -0
  2455. psychopy/tests/data/greyscaleLowContr_cm.png +0 -0
  2456. psychopy/tests/data/greyscaleLowContr_deg.png +0 -0
  2457. psychopy/tests/data/greyscaleLowContr_degFlat.png +0 -0
  2458. psychopy/tests/data/greyscaleLowContr_degFlatPos.png +0 -0
  2459. psychopy/tests/data/greyscaleLowContr_height.png +0 -0
  2460. psychopy/tests/data/greyscaleLowContr_norm.png +0 -0
  2461. psychopy/tests/data/greyscaleLowContr_normAddBlend.png +0 -0
  2462. psychopy/tests/data/greyscaleLowContr_normHexbackground.png +0 -0
  2463. psychopy/tests/data/greyscaleLowContr_normNoShade.png +0 -0
  2464. psychopy/tests/data/greyscaleLowContr_pix.png +0 -0
  2465. psychopy/tests/data/greyscaleLowContr_stencil.png +0 -0
  2466. psychopy/tests/data/greyscale_cm.png +0 -0
  2467. psychopy/tests/data/greyscale_deg.png +0 -0
  2468. psychopy/tests/data/greyscale_degFlat.png +0 -0
  2469. psychopy/tests/data/greyscale_degFlatPos.png +0 -0
  2470. psychopy/tests/data/greyscale_height.png +0 -0
  2471. psychopy/tests/data/greyscale_norm.png +0 -0
  2472. psychopy/tests/data/greyscale_normAddBlend.png +0 -0
  2473. psychopy/tests/data/greyscale_normHexbackground.png +0 -0
  2474. psychopy/tests/data/greyscale_normNoShade.png +0 -0
  2475. psychopy/tests/data/greyscale_pix.png +0 -0
  2476. psychopy/tests/data/greyscale_stencil.png +0 -0
  2477. psychopy/tests/data/imageAndGauss_cm.png +0 -0
  2478. psychopy/tests/data/imageAndGauss_deg.png +0 -0
  2479. psychopy/tests/data/imageAndGauss_degFlat.png +0 -0
  2480. psychopy/tests/data/imageAndGauss_degFlatPos.png +0 -0
  2481. psychopy/tests/data/imageAndGauss_height.png +0 -0
  2482. psychopy/tests/data/imageAndGauss_norm.png +0 -0
  2483. psychopy/tests/data/imageAndGauss_normAddBlend.png +0 -0
  2484. psychopy/tests/data/imageAndGauss_normHexbackground.png +0 -0
  2485. psychopy/tests/data/imageAndGauss_normNoShade.png +0 -0
  2486. psychopy/tests/data/imageAndGauss_pix.png +0 -0
  2487. psychopy/tests/data/imageAndGauss_stencil.png +0 -0
  2488. psychopy/tests/data/movFrame1_cm.png +0 -0
  2489. psychopy/tests/data/movFrame1_deg.png +0 -0
  2490. psychopy/tests/data/movFrame1_degFlat.png +0 -0
  2491. psychopy/tests/data/movFrame1_degFlatPos.png +0 -0
  2492. psychopy/tests/data/movFrame1_height.png +0 -0
  2493. psychopy/tests/data/movFrame1_norm.png +0 -0
  2494. psychopy/tests/data/movFrame1_normAddBlend.png +0 -0
  2495. psychopy/tests/data/movFrame1_normHexbackground.png +0 -0
  2496. psychopy/tests/data/movFrame1_normNoShade.png +0 -0
  2497. psychopy/tests/data/movFrame1_pix.png +0 -0
  2498. psychopy/tests/data/movFrame1_stencil.png +0 -0
  2499. psychopy/tests/data/multiKeypressExperiment.psydat +357 -0
  2500. psychopy/tests/data/multiKeypressTrialhandler.psydat +252 -0
  2501. psychopy/tests/data/multiStairConds.xlsx +0 -0
  2502. psychopy/tests/data/multiStairQuestPlus.xlsx +0 -0
  2503. psychopy/tests/data/newstyle.psydat +6155 -0
  2504. psychopy/tests/data/noiseAndRcos_cm.png +0 -0
  2505. psychopy/tests/data/noiseAndRcos_deg.png +0 -0
  2506. psychopy/tests/data/noiseAndRcos_degFlat.png +0 -0
  2507. psychopy/tests/data/noiseAndRcos_degFlatPos.png +0 -0
  2508. psychopy/tests/data/noiseAndRcos_height.png +0 -0
  2509. psychopy/tests/data/noiseAndRcos_norm.png +0 -0
  2510. psychopy/tests/data/noiseAndRcos_normAddBlend.png +0 -0
  2511. psychopy/tests/data/noiseAndRcos_normHexbackground.png +0 -0
  2512. psychopy/tests/data/noiseAndRcos_normNoShade.png +0 -0
  2513. psychopy/tests/data/noiseAndRcos_pix.png +0 -0
  2514. psychopy/tests/data/noiseAndRcos_stencil.png +0 -0
  2515. psychopy/tests/data/noiseFiltersAndRcos_cm.png +0 -0
  2516. psychopy/tests/data/noiseFiltersAndRcos_deg.png +0 -0
  2517. psychopy/tests/data/noiseFiltersAndRcos_degFlat.png +0 -0
  2518. psychopy/tests/data/noiseFiltersAndRcos_degFlatPos.png +0 -0
  2519. psychopy/tests/data/noiseFiltersAndRcos_height.png +0 -0
  2520. psychopy/tests/data/noiseFiltersAndRcos_norm.png +0 -0
  2521. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend.png +0 -0
  2522. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground.png +0 -0
  2523. psychopy/tests/data/noiseFiltersAndRcos_normNoShade.png +0 -0
  2524. psychopy/tests/data/noiseFiltersAndRcos_pix.png +0 -0
  2525. psychopy/tests/data/noiseFiltersAndRcos_stencil.png +0 -0
  2526. psychopy/tests/data/numpyImage_cm.png +0 -0
  2527. psychopy/tests/data/numpyImage_deg.png +0 -0
  2528. psychopy/tests/data/numpyImage_degFlat.png +0 -0
  2529. psychopy/tests/data/numpyImage_degFlatPos.png +0 -0
  2530. psychopy/tests/data/numpyImage_height.png +0 -0
  2531. psychopy/tests/data/numpyImage_norm.png +0 -0
  2532. psychopy/tests/data/numpyImage_normAddBlend.png +0 -0
  2533. psychopy/tests/data/numpyImage_normHexbackground.png +0 -0
  2534. psychopy/tests/data/numpyImage_normNoShade.png +0 -0
  2535. psychopy/tests/data/numpyImage_pix.png +0 -0
  2536. psychopy/tests/data/numpyImage_stencil.png +0 -0
  2537. psychopy/tests/data/numpyLowContr_cm.png +0 -0
  2538. psychopy/tests/data/numpyLowContr_deg.png +0 -0
  2539. psychopy/tests/data/numpyLowContr_degFlat.png +0 -0
  2540. psychopy/tests/data/numpyLowContr_degFlatPos.png +0 -0
  2541. psychopy/tests/data/numpyLowContr_height.png +0 -0
  2542. psychopy/tests/data/numpyLowContr_norm.png +0 -0
  2543. psychopy/tests/data/numpyLowContr_normAddBlend.png +0 -0
  2544. psychopy/tests/data/numpyLowContr_normHexbackground.png +0 -0
  2545. psychopy/tests/data/numpyLowContr_normNoShade.png +0 -0
  2546. psychopy/tests/data/numpyLowContr_pix.png +0 -0
  2547. psychopy/tests/data/numpyLowContr_stencil.png +0 -0
  2548. psychopy/tests/data/oldstyle.psydat +1534 -0
  2549. psychopy/tests/data/oldstyle_stair.psydat +263 -0
  2550. psychopy/tests/data/ratingscale1_cm.png +0 -0
  2551. psychopy/tests/data/ratingscale1_deg.png +0 -0
  2552. psychopy/tests/data/ratingscale1_degFlat.png +0 -0
  2553. psychopy/tests/data/ratingscale1_degFlatPos.png +0 -0
  2554. psychopy/tests/data/ratingscale1_height.png +0 -0
  2555. psychopy/tests/data/ratingscale1_norm.png +0 -0
  2556. psychopy/tests/data/ratingscale1_normAddBlend.png +0 -0
  2557. psychopy/tests/data/ratingscale1_normHexbackground.png +0 -0
  2558. psychopy/tests/data/ratingscale1_normNoShade.png +0 -0
  2559. psychopy/tests/data/ratingscale1_pix.png +0 -0
  2560. psychopy/tests/data/ratingscale1_stencil.png +0 -0
  2561. psychopy/tests/data/red_16000.flac.dist +0 -0
  2562. psychopy/tests/data/retroListParam.psyexp +82 -0
  2563. psychopy/tests/data/right_to_left_unidcode.xlsx +0 -0
  2564. psychopy/tests/data/sample.meshwarp.data +11522 -0
  2565. psychopy/tests/data/shape2_1_cm.png +0 -0
  2566. psychopy/tests/data/shape2_1_deg.png +0 -0
  2567. psychopy/tests/data/shape2_1_degFlat.png +0 -0
  2568. psychopy/tests/data/shape2_1_degFlatPos.png +0 -0
  2569. psychopy/tests/data/shape2_1_height.png +0 -0
  2570. psychopy/tests/data/shape2_1_norm.png +0 -0
  2571. psychopy/tests/data/shape2_1_normAddBlend.png +0 -0
  2572. psychopy/tests/data/shape2_1_normHexbackground.png +0 -0
  2573. psychopy/tests/data/shape2_1_normNoShade.png +0 -0
  2574. psychopy/tests/data/shape2_1_pix.png +0 -0
  2575. psychopy/tests/data/shape2_1_stencil.png +0 -0
  2576. psychopy/tests/data/shape2_2_cm.png +0 -0
  2577. psychopy/tests/data/shape2_2_deg.png +0 -0
  2578. psychopy/tests/data/shape2_2_degFlat.png +0 -0
  2579. psychopy/tests/data/shape2_2_degFlatPos.png +0 -0
  2580. psychopy/tests/data/shape2_2_height.png +0 -0
  2581. psychopy/tests/data/shape2_2_norm.png +0 -0
  2582. psychopy/tests/data/shape2_2_normAddBlend.png +0 -0
  2583. psychopy/tests/data/shape2_2_normHexbackground.png +0 -0
  2584. psychopy/tests/data/shape2_2_normNoShade.png +0 -0
  2585. psychopy/tests/data/shape2_2_pix.png +0 -0
  2586. psychopy/tests/data/shape2_2_stencil.png +0 -0
  2587. psychopy/tests/data/simpleimage1_cm.png +0 -0
  2588. psychopy/tests/data/simpleimage1_deg.png +0 -0
  2589. psychopy/tests/data/simpleimage1_degFlat.png +0 -0
  2590. psychopy/tests/data/simpleimage1_degFlatPos.png +0 -0
  2591. psychopy/tests/data/simpleimage1_height.png +0 -0
  2592. psychopy/tests/data/simpleimage1_norm.png +0 -0
  2593. psychopy/tests/data/simpleimage1_normAddBlend.png +0 -0
  2594. psychopy/tests/data/simpleimage1_normHexbackground.png +0 -0
  2595. psychopy/tests/data/simpleimage1_normNoShade.png +0 -0
  2596. psychopy/tests/data/simpleimage1_pix.png +0 -0
  2597. psychopy/tests/data/simpleimage1_stencil.png +0 -0
  2598. psychopy/tests/data/test001EntryImporting.psyexp +66 -0
  2599. psychopy/tests/data/testMovie.mp4 +0 -0
  2600. psychopy/tests/data/test_basic_run.py +1 -0
  2601. psychopy/tests/data/test_circle_radius_.1height.png +0 -0
  2602. psychopy/tests/data/test_circle_radius_.2height.png +0 -0
  2603. psychopy/tests/data/test_circle_radius_10pix.png +0 -0
  2604. psychopy/tests/data/test_circle_radius_20pix.png +0 -0
  2605. psychopy/tests/data/test_components/testClearKeyboard/testClearKeyboard.psyexp +200 -0
  2606. psychopy/tests/data/test_form_combinations_choice_bigItem.png +0 -0
  2607. psychopy/tests/data/test_form_combinations_choice_bigItemOverflow.png +0 -0
  2608. psychopy/tests/data/test_form_combinations_choice_bigResp.png +0 -0
  2609. psychopy/tests/data/test_form_combinations_choice_bigRespOverflow.png +0 -0
  2610. psychopy/tests/data/test_form_combinations_description_bigItem.png +0 -0
  2611. psychopy/tests/data/test_form_combinations_description_bigItemOverflow.png +0 -0
  2612. psychopy/tests/data/test_form_combinations_description_bigResp.png +0 -0
  2613. psychopy/tests/data/test_form_combinations_description_bigRespOverflow.png +0 -0
  2614. psychopy/tests/data/test_form_combinations_free text_bigItem.png +0 -0
  2615. psychopy/tests/data/test_form_combinations_free text_bigItemOverflow.png +0 -0
  2616. psychopy/tests/data/test_form_combinations_free text_bigResp.png +0 -0
  2617. psychopy/tests/data/test_form_combinations_free text_bigRespOverflow.png +0 -0
  2618. psychopy/tests/data/test_form_combinations_heading_bigItem.png +0 -0
  2619. psychopy/tests/data/test_form_combinations_heading_bigItemOverflow.png +0 -0
  2620. psychopy/tests/data/test_form_combinations_heading_bigResp.png +0 -0
  2621. psychopy/tests/data/test_form_combinations_heading_bigRespOverflow.png +0 -0
  2622. psychopy/tests/data/test_form_combinations_radio_bigItem.png +0 -0
  2623. psychopy/tests/data/test_form_combinations_radio_bigItemOverflow.png +0 -0
  2624. psychopy/tests/data/test_form_combinations_radio_bigResp.png +0 -0
  2625. psychopy/tests/data/test_form_combinations_radio_bigRespOverflow.png +0 -0
  2626. psychopy/tests/data/test_form_combinations_rating_bigItem.png +0 -0
  2627. psychopy/tests/data/test_form_combinations_rating_bigItemOverflow.png +0 -0
  2628. psychopy/tests/data/test_form_combinations_rating_bigResp.png +0 -0
  2629. psychopy/tests/data/test_form_combinations_rating_bigRespOverflow.png +0 -0
  2630. psychopy/tests/data/test_form_combinations_slider_bigItem.png +0 -0
  2631. psychopy/tests/data/test_form_combinations_slider_bigItemOverflow.png +0 -0
  2632. psychopy/tests/data/test_form_combinations_slider_bigResp.png +0 -0
  2633. psychopy/tests/data/test_form_combinations_slider_bigRespOverflow.png +0 -0
  2634. psychopy/tests/data/test_get_resources/blue.png +0 -0
  2635. psychopy/tests/data/test_get_resources/groupA.csv +3 -0
  2636. psychopy/tests/data/test_get_resources/groupB.csv +3 -0
  2637. psychopy/tests/data/test_get_resources/groups.csv +3 -0
  2638. psychopy/tests/data/test_get_resources/handledbyrm_constrloop.psyexp +143 -0
  2639. psychopy/tests/data/test_get_resources/handledbyrm_noloop.psyexp +114 -0
  2640. psychopy/tests/data/test_get_resources/handledbyrm_recurloop.psyexp +138 -0
  2641. psychopy/tests/data/test_get_resources/handledbyrm_strloop.psyexp +126 -0
  2642. psychopy/tests/data/test_get_resources/handledbystatic_constrloop.psyexp +141 -0
  2643. psychopy/tests/data/test_get_resources/handledbystatic_noloop.psyexp +112 -0
  2644. psychopy/tests/data/test_get_resources/handledbystatic_recurloop.psyexp +136 -0
  2645. psychopy/tests/data/test_get_resources/handledbystatic_strloop.psyexp +124 -0
  2646. psychopy/tests/data/test_get_resources/unhandled_constrloop.psyexp +128 -0
  2647. psychopy/tests/data/test_get_resources/unhandled_noloop.psyexp +99 -0
  2648. psychopy/tests/data/test_get_resources/unhandled_recurloop.psyexp +123 -0
  2649. psychopy/tests/data/test_get_resources/unhandled_strloop.psyexp +111 -0
  2650. psychopy/tests/data/test_get_resources/white.png +0 -0
  2651. psychopy/tests/data/test_get_resources/yellow.png +0 -0
  2652. psychopy/tests/data/test_image_aspect_default_None.png +0 -0
  2653. psychopy/tests/data/test_image_aspect_default_xFull_yNone.png +0 -0
  2654. psychopy/tests/data/test_image_aspect_default_xNone_yFull.png +0 -0
  2655. psychopy/tests/data/test_image_aspect_default_xNone_yNone.png +0 -0
  2656. psychopy/tests/data/test_image_flip_anchor_horiz.png +0 -0
  2657. psychopy/tests/data/test_image_flip_anchor_vert.png +0 -0
  2658. psychopy/tests/data/test_loaded_namespace/test_counterbalance.psyexp +142 -0
  2659. psychopy/tests/data/test_loaded_namespace/test_custom_missing.psyexp +129 -0
  2660. psychopy/tests/data/test_loaded_namespace/test_missing_counterbalance.psyexp +116 -0
  2661. psychopy/tests/data/test_loaded_namespace/test_mix_exp.psyexp +181 -0
  2662. psychopy/tests/data/test_loaded_namespace/test_mix_missing.psyexp +140 -0
  2663. psychopy/tests/data/test_loaded_namespace/test_mix_name_calibration.psyexp +164 -0
  2664. psychopy/tests/data/test_loops/testLoopsBlocks.psyexp +161 -0
  2665. psychopy/tests/data/test_loops/testStaircase.psyexp +105 -0
  2666. psychopy/tests/data/test_loops/test_current_loop_attr.psyexp +185 -0
  2667. psychopy/tests/data/test_panorama/panoramaTestImage.png +0 -0
  2668. psychopy/tests/data/test_panorama/panoramaTestImage.svg +1 -0
  2669. psychopy/tests/data/test_panorama/testPanorama_mvmt_-0.3_-0.3.png +0 -0
  2670. psychopy/tests/data/test_panorama/testPanorama_mvmt_-0.3_0.3.png +0 -0
  2671. psychopy/tests/data/test_panorama/testPanorama_mvmt_-1.0_-0.3.png +0 -0
  2672. psychopy/tests/data/test_panorama/testPanorama_mvmt_-1.0_0.3.png +0 -0
  2673. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.0_-1.0.png +0 -0
  2674. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.0_1.0.png +0 -0
  2675. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.3_-0.3.png +0 -0
  2676. psychopy/tests/data/test_panorama/testPanorama_mvmt_0.3_0.3.png +0 -0
  2677. psychopy/tests/data/test_resources.psyexp +491 -0
  2678. psychopy/tests/data/test_session/outside_root/externalExp.psyexp +116 -0
  2679. psychopy/tests/data/test_session/root/annotation/annotation.psyexp +161 -0
  2680. psychopy/tests/data/test_session/root/error/error.psyexp +93 -0
  2681. psychopy/tests/data/test_session/root/exp1/exp1.psyexp +133 -0
  2682. psychopy/tests/data/test_session/root/exp2/exp2.psyexp +133 -0
  2683. psychopy/tests/data/test_session/root/frameRate/frameRate.psyexp +114 -0
  2684. psychopy/tests/data/test_session/root/invUseVersion/invUseVersion.psyexp +133 -0
  2685. psychopy/tests/data/test_session/root/testClockFormat/testClockFormat.psyexp +122 -0
  2686. psychopy/tests/data/test_session/root/testCtrls/testCtrls.psyexp +115 -0
  2687. psychopy/tests/data/test_session/root/testEditExpInfo/testEditExpInfo.psyexp +135 -0
  2688. psychopy/tests/data/test_session/root/testFutureTrials/testFutureTrials.psyexp +155 -0
  2689. psychopy/tests/data/test_session/root/testNamedButtonBox/testNamedButtonBox.psyexp +145 -0
  2690. psychopy/tests/data/test_session/root/testTrialNav/trialNav.psyexp +158 -0
  2691. psychopy/tests/data/test_slider_horiz_accute_horiz.png +0 -0
  2692. psychopy/tests/data/test_slider_horiz_accute_vert.png +0 -0
  2693. psychopy/tests/data/test_slider_horiz_horiz.png +0 -0
  2694. psychopy/tests/data/test_slider_horiz_obtuse_horiz.png +0 -0
  2695. psychopy/tests/data/test_slider_horiz_obtuse_vert.png +0 -0
  2696. psychopy/tests/data/test_slider_horiz_vert.png +0 -0
  2697. psychopy/tests/data/test_slider_ticklabelloc_blanks.png +0 -0
  2698. psychopy/tests/data/test_slider_ticklabelloc_clustered.png +0 -0
  2699. psychopy/tests/data/test_slider_ticklabelloc_morelabels.png +0 -0
  2700. psychopy/tests/data/test_slider_ticklabelloc_morelabelsclustered.png +0 -0
  2701. psychopy/tests/data/test_slider_ticklabelloc_moreticks.png +0 -0
  2702. psychopy/tests/data/test_slider_ticklabelloc_moreticksclustered.png +0 -0
  2703. psychopy/tests/data/test_slider_ticklabelloc_nolabels.png +0 -0
  2704. psychopy/tests/data/test_slider_ticklabelloc_noticks.png +0 -0
  2705. psychopy/tests/data/test_slider_ticklabelloc_simple.png +0 -0
  2706. psychopy/tests/data/test_slider_triangle_horiz_False_flip_False.png +0 -0
  2707. psychopy/tests/data/test_slider_triangle_horiz_False_flip_True.png +0 -0
  2708. psychopy/tests/data/test_slider_triangle_horiz_True_flip_False.png +0 -0
  2709. psychopy/tests/data/test_slider_triangle_horiz_True_flip_True.png +0 -0
  2710. psychopy/tests/data/test_sounds/default_16000.wav +0 -0
  2711. psychopy/tests/data/test_sounds/default_192000.wav +0 -0
  2712. psychopy/tests/data/test_sounds/default_22050.wav +0 -0
  2713. psychopy/tests/data/test_sounds/default_32000.wav +0 -0
  2714. psychopy/tests/data/test_sounds/default_44100.wav +0 -0
  2715. psychopy/tests/data/test_sounds/default_48000.wav +0 -0
  2716. psychopy/tests/data/test_sounds/default_8000.wav +0 -0
  2717. psychopy/tests/data/test_sounds/default_96000.wav +0 -0
  2718. psychopy/tests/data/test_static_component_script.psyexp +76 -0
  2719. psychopy/tests/data/test_win_bg_large_256_default.png +0 -0
  2720. psychopy/tests/data/test_win_bg_large_500_default.png +0 -0
  2721. psychopy/tests/data/test_win_bg_small_200_default.png +0 -0
  2722. psychopy/tests/data/test_win_bg_small_256_default.png +0 -0
  2723. psychopy/tests/data/test_win_bg_tall_200_default.png +0 -0
  2724. psychopy/tests/data/test_win_bg_tall_256_default.png +0 -0
  2725. psychopy/tests/data/test_win_bg_tall_500_default.png +0 -0
  2726. psychopy/tests/data/test_win_bg_tall_fill_default.png +0 -0
  2727. psychopy/tests/data/test_win_bg_wide_200_default.png +0 -0
  2728. psychopy/tests/data/test_win_bg_wide_256_default.png +0 -0
  2729. psychopy/tests/data/test_win_bg_wide_500_default.png +0 -0
  2730. psychopy/tests/data/test_win_bg_wide_fill_default.png +0 -0
  2731. psychopy/tests/data/test_win_bgcolor_blue.png +0 -0
  2732. psychopy/tests/data/test_win_bgcolor_green.png +0 -0
  2733. psychopy/tests/data/test_win_bgcolor_red.png +0 -0
  2734. psychopy/tests/data/testimage.jpg +0 -0
  2735. psychopy/tests/data/testimagegray.jpg +0 -0
  2736. psychopy/tests/data/testpixels.png +0 -0
  2737. psychopy/tests/data/testwedges.png +0 -0
  2738. psychopy/tests/data/text1_cm.png +0 -0
  2739. psychopy/tests/data/text1_deg.png +0 -0
  2740. psychopy/tests/data/text1_degFlat.png +0 -0
  2741. psychopy/tests/data/text1_degFlatPos.png +0 -0
  2742. psychopy/tests/data/text1_height.png +0 -0
  2743. psychopy/tests/data/text1_norm.png +0 -0
  2744. psychopy/tests/data/text1_normAddBlend.png +0 -0
  2745. psychopy/tests/data/text1_normHexbackground.png +0 -0
  2746. psychopy/tests/data/text1_normNoShade.png +0 -0
  2747. psychopy/tests/data/text1_pix.png +0 -0
  2748. psychopy/tests/data/text1_stencil.png +0 -0
  2749. psychopy/tests/data/text2_cm.png +0 -0
  2750. psychopy/tests/data/text2_deg.png +0 -0
  2751. psychopy/tests/data/text2_degFlat.png +0 -0
  2752. psychopy/tests/data/text2_degFlatPos.png +0 -0
  2753. psychopy/tests/data/text2_norm.png +0 -0
  2754. psychopy/tests/data/text2_normNoShade.png +0 -0
  2755. psychopy/tests/data/text2_pix.png +0 -0
  2756. psychopy/tests/data/textbox_charcolors_default_roygbiv.png +0 -0
  2757. psychopy/tests/data/textbox_charcolors_default_white_hello_black_there.png +0 -0
  2758. psychopy/tests/data/textbox_default_align_bottom_center.png +0 -0
  2759. psychopy/tests/data/textbox_default_align_bottom_left.png +0 -0
  2760. psychopy/tests/data/textbox_default_align_bottom_right.png +0 -0
  2761. psychopy/tests/data/textbox_default_align_center.png +0 -0
  2762. psychopy/tests/data/textbox_default_align_center_center.png +0 -0
  2763. psychopy/tests/data/textbox_default_align_center_left.png +0 -0
  2764. psychopy/tests/data/textbox_default_align_center_right.png +0 -0
  2765. psychopy/tests/data/textbox_default_align_centre.png +0 -0
  2766. psychopy/tests/data/textbox_default_align_centre_centre.png +0 -0
  2767. psychopy/tests/data/textbox_default_align_more_than_two_words.png +0 -0
  2768. psychopy/tests/data/textbox_default_align_someword.png +0 -0
  2769. psychopy/tests/data/textbox_default_align_top_center.png +0 -0
  2770. psychopy/tests/data/textbox_default_align_top_left.png +0 -0
  2771. psychopy/tests/data/textbox_default_align_top_right.png +0 -0
  2772. psychopy/tests/data/textbox_default_colors_WOB.png +0 -0
  2773. psychopy/tests/data/textbox_default_colors_exemplar1.png +0 -0
  2774. psychopy/tests/data/textbox_default_colors_exemplar2.png +0 -0
  2775. psychopy/tests/data/textbox_default_colors_exemplar3.png +0 -0
  2776. psychopy/tests/data/textbox_default_colors_tyke1.png +0 -0
  2777. psychopy/tests/data/textbox_default_colors_tyke2.png +0 -0
  2778. psychopy/tests/data/textbox_default_colors_tyke3.png +0 -0
  2779. psychopy/tests/data/textbox_default_cutoff_top.png +0 -0
  2780. psychopy/tests/data/textbox_default_exemplar_1.png +0 -0
  2781. psychopy/tests/data/textbox_default_exemplar_2.png +0 -0
  2782. psychopy/tests/data/textbox_default_exemplar_3.png +0 -0
  2783. psychopy/tests/data/textbox_default_exemplar_4.png +0 -0
  2784. psychopy/tests/data/textbox_default_tyke_1.png +0 -0
  2785. psychopy/tests/data/textbox_default_tyke_2.png +0 -0
  2786. psychopy/tests/data/textbox_typing_blank.png +0 -0
  2787. psychopy/tests/data/textbox_typing_longKoeran.png +0 -0
  2788. psychopy/tests/data/textbox_typing_longWord.png +0 -0
  2789. psychopy/tests/data/textbox_typing_navDel.png +0 -0
  2790. psychopy/tests/data/textbox_typing_navLR.png +0 -0
  2791. psychopy/tests/data/textbox_typing_newline.png +0 -0
  2792. psychopy/tests/data/textbox_typing_pangram.png +0 -0
  2793. psychopy/tests/data/textbox_uax14_align_bottom_center.png +0 -0
  2794. psychopy/tests/data/textbox_uax14_align_bottom_left.png +0 -0
  2795. psychopy/tests/data/textbox_uax14_align_bottom_right.png +0 -0
  2796. psychopy/tests/data/textbox_uax14_align_center.png +0 -0
  2797. psychopy/tests/data/textbox_uax14_align_center_center.png +0 -0
  2798. psychopy/tests/data/textbox_uax14_align_center_left.png +0 -0
  2799. psychopy/tests/data/textbox_uax14_align_center_right.png +0 -0
  2800. psychopy/tests/data/textbox_uax14_align_centre.png +0 -0
  2801. psychopy/tests/data/textbox_uax14_align_centre_centre.png +0 -0
  2802. psychopy/tests/data/textbox_uax14_align_more_than_two_words.png +0 -0
  2803. psychopy/tests/data/textbox_uax14_align_someword.png +0 -0
  2804. psychopy/tests/data/textbox_uax14_align_top_center.png +0 -0
  2805. psychopy/tests/data/textbox_uax14_align_top_left.png +0 -0
  2806. psychopy/tests/data/textbox_uax14_align_top_right.png +0 -0
  2807. psychopy/tests/data/textbox_uax14_colors_WOB.png +0 -0
  2808. psychopy/tests/data/textbox_uax14_colors_exemplar1.png +0 -0
  2809. psychopy/tests/data/textbox_uax14_colors_exemplar2.png +0 -0
  2810. psychopy/tests/data/textbox_uax14_colors_exemplar3.png +0 -0
  2811. psychopy/tests/data/textbox_uax14_colors_tyke1.png +0 -0
  2812. psychopy/tests/data/textbox_uax14_colors_tyke2.png +0 -0
  2813. psychopy/tests/data/textbox_uax14_colors_tyke3.png +0 -0
  2814. psychopy/tests/data/textbox_uax14_cutoff_top.png +0 -0
  2815. psychopy/tests/data/textbox_uax14_exemplar_1.png +0 -0
  2816. psychopy/tests/data/textbox_uax14_exemplar_2.png +0 -0
  2817. psychopy/tests/data/textbox_uax14_exemplar_3.png +0 -0
  2818. psychopy/tests/data/textbox_uax14_exemplar_4.png +0 -0
  2819. psychopy/tests/data/textbox_uax14_tyke_1.png +0 -0
  2820. psychopy/tests/data/textbox_uax14_tyke_2.png +0 -0
  2821. psychopy/tests/data/trialTypes.csv +1 -0
  2822. psychopy/tests/data/trialTypes.docx +0 -0
  2823. psychopy/tests/data/trialTypes.pkl +0 -0
  2824. psychopy/tests/data/trialTypes.tsv +7 -0
  2825. psychopy/tests/data/trialTypes.xls +0 -0
  2826. psychopy/tests/data/trialTypes.xlsx +0 -0
  2827. psychopy/tests/data/trialTypes_eu.csv +7 -0
  2828. psychopy/tests/data/trialsBlankCols.xlsx +0 -0
  2829. psychopy/tests/data/wedge1_cm.png +0 -0
  2830. psychopy/tests/data/wedge1_deg.png +0 -0
  2831. psychopy/tests/data/wedge1_degFlat.png +0 -0
  2832. psychopy/tests/data/wedge1_degFlatPos.png +0 -0
  2833. psychopy/tests/data/wedge1_height.png +0 -0
  2834. psychopy/tests/data/wedge1_norm.png +0 -0
  2835. psychopy/tests/data/wedge1_normAddBlend.png +0 -0
  2836. psychopy/tests/data/wedge1_normHexbackground.png +0 -0
  2837. psychopy/tests/data/wedge1_normNoShade.png +0 -0
  2838. psychopy/tests/data/wedge1_pix.png +0 -0
  2839. psychopy/tests/data/wedge1_stencil.png +0 -0
  2840. psychopy/tests/data/wedge2_cm.png +0 -0
  2841. psychopy/tests/data/wedge2_deg.png +0 -0
  2842. psychopy/tests/data/wedge2_degFlat.png +0 -0
  2843. psychopy/tests/data/wedge2_degFlatPos.png +0 -0
  2844. psychopy/tests/data/wedge2_height.png +0 -0
  2845. psychopy/tests/data/wedge2_norm.png +0 -0
  2846. psychopy/tests/data/wedge2_normAddBlend.png +0 -0
  2847. psychopy/tests/data/wedge2_normHexbackground.png +0 -0
  2848. psychopy/tests/data/wedge2_normNoShade.png +0 -0
  2849. psychopy/tests/data/wedge2_pix.png +0 -0
  2850. psychopy/tests/data/wedge2_stencil.png +0 -0
  2851. psychopy/tests/data/winScalePos_ori0_scale-1_-1_pos-0.4_0.png +0 -0
  2852. psychopy/tests/data/winScalePos_ori0_scale-1_-1_pos0_0.png +0 -0
  2853. psychopy/tests/data/winScalePos_ori0_scale-1_1_pos-0.4_0.png +0 -0
  2854. psychopy/tests/data/winScalePos_ori0_scale-1_1_pos0_0.png +0 -0
  2855. psychopy/tests/data/winScalePos_ori0_scale-2_-2_pos-0.4_0.png +0 -0
  2856. psychopy/tests/data/winScalePos_ori0_scale-2_-2_pos0_0.png +0 -0
  2857. psychopy/tests/data/winScalePos_ori0_scale-2_2_pos-0.4_0.png +0 -0
  2858. psychopy/tests/data/winScalePos_ori0_scale-2_2_pos0_0.png +0 -0
  2859. psychopy/tests/data/winScalePos_ori0_scale1_-1_pos-0.4_0.png +0 -0
  2860. psychopy/tests/data/winScalePos_ori0_scale1_-1_pos0_0.png +0 -0
  2861. psychopy/tests/data/winScalePos_ori0_scale1_1_pos-0.4_0.png +0 -0
  2862. psychopy/tests/data/winScalePos_ori0_scale1_1_pos0_0.png +0 -0
  2863. psychopy/tests/data/winScalePos_ori0_scale2_-2_pos-0.4_0.png +0 -0
  2864. psychopy/tests/data/winScalePos_ori0_scale2_-2_pos0_0.png +0 -0
  2865. psychopy/tests/data/winScalePos_ori0_scale2_2_pos-0.4_0.png +0 -0
  2866. psychopy/tests/data/winScalePos_ori0_scale2_2_pos0_0.png +0 -0
  2867. psychopy/tests/data/winScalePos_ori45_scale-1_-1_pos0_0.png +0 -0
  2868. psychopy/tests/data/winScalePos_ori45_scale-1_1_pos0_0.png +0 -0
  2869. psychopy/tests/data/winScalePos_ori45_scale-2_-2_pos0_0.png +0 -0
  2870. psychopy/tests/data/winScalePos_ori45_scale-2_2_pos0_0.png +0 -0
  2871. psychopy/tests/data/winScalePos_ori45_scale1_-1_pos0_0.png +0 -0
  2872. psychopy/tests/data/winScalePos_ori45_scale1_1_pos0_0.png +0 -0
  2873. psychopy/tests/data/winScalePos_ori45_scale2_-2_pos0_0.png +0 -0
  2874. psychopy/tests/data/winScalePos_ori45_scale2_2_pos0_0.png +0 -0
  2875. psychopy/tests/doc/run_btn.png +0 -0
  2876. psychopy/tests/doc/test-suite-bugs.jpg +0 -0
  2877. psychopy/tests/doc/user-bugs.jpg +0 -0
  2878. psychopy/tests/dummy_xorg.conf +21 -0
  2879. psychopy/tests/run.py +50 -0
  2880. psychopy/tests/test_Installation.py +38 -0
  2881. psychopy/tests/test_alerts/test_alerts.py +37 -0
  2882. psychopy/tests/test_alerts/test_alerttools.py +132 -0
  2883. psychopy/tests/test_app/__init__.py +0 -0
  2884. psychopy/tests/test_app/conftest.py +40 -0
  2885. psychopy/tests/test_app/test_builder/__init__.py +0 -0
  2886. psychopy/tests/test_app/test_builder/test_BuilderFrame.py +166 -0
  2887. psychopy/tests/test_app/test_builder/test_CompileFromBuilder.py +133 -0
  2888. psychopy/tests/test_app/test_builder/test_ComponentDialogs.py +98 -0
  2889. psychopy/tests/test_app/test_command_line.py +65 -0
  2890. psychopy/tests/test_app/test_runner/__init__.py +0 -0
  2891. psychopy/tests/test_app/test_runner/test_RunnerFrame.py +166 -0
  2892. psychopy/tests/test_app/test_speed.py +147 -0
  2893. psychopy/tests/test_app/test_themes/test_icons.py +79 -0
  2894. psychopy/tests/test_codecov.py +29 -0
  2895. psychopy/tests/test_colors/test_Color.py +41 -0
  2896. psychopy/tests/test_data/dataPrev.xlsx +0 -0
  2897. psychopy/tests/test_data/test_ExperimentHandler.py +171 -0
  2898. psychopy/tests/test_data/test_MultiStairHandler.py +174 -0
  2899. psychopy/tests/test_data/test_StairHandlers.py +1241 -0
  2900. psychopy/tests/test_data/test_TrialHandler.py +335 -0
  2901. psychopy/tests/test_data/test_TrialHandler2.py +584 -0
  2902. psychopy/tests/test_data/test_TrialHandlerExt.py +359 -0
  2903. psychopy/tests/test_data/test_fitFunctions.py +101 -0
  2904. psychopy/tests/test_data/test_utils.py +173 -0
  2905. psychopy/tests/test_data/test_xlsx.py +92 -0
  2906. psychopy/tests/test_demos/test_builder_demos.py +52 -0
  2907. psychopy/tests/test_experiment/__init__.py +0 -0
  2908. psychopy/tests/test_experiment/known_py_diffs.txt +47 -0
  2909. psychopy/tests/test_experiment/needs_wx/__init__.py +0 -0
  2910. psychopy/tests/test_experiment/needs_wx/componsTemplate.txt +14780 -0
  2911. psychopy/tests/test_experiment/needs_wx/genComponsTemplate.py +130 -0
  2912. psychopy/tests/test_experiment/needs_wx/test_Experiment.py +406 -0
  2913. psychopy/tests/test_experiment/needs_wx/test_components.py +170 -0
  2914. psychopy/tests/test_experiment/test_component_compile_js.py +59 -0
  2915. psychopy/tests/test_experiment/test_component_compile_python.py +165 -0
  2916. psychopy/tests/test_experiment/test_components/__init__.py +1 -0
  2917. psychopy/tests/test_experiment/test_components/test_ButtonBoxComponent.py +218 -0
  2918. psychopy/tests/test_experiment/test_components/test_CodeComponent.py +70 -0
  2919. psychopy/tests/test_experiment/test_components/test_GratingComponent.py +7 -0
  2920. psychopy/tests/test_experiment/test_components/test_ImageComponent.py +8 -0
  2921. psychopy/tests/test_experiment/test_components/test_KeyboardComponent.py +28 -0
  2922. psychopy/tests/test_experiment/test_components/test_MouseComponent.py +136 -0
  2923. psychopy/tests/test_experiment/test_components/test_PolygonComponent.py +65 -0
  2924. psychopy/tests/test_experiment/test_components/test_ResourceManagerComponent.py +50 -0
  2925. psychopy/tests/test_experiment/test_components/test_RoutineSettingsComponent.py +16 -0
  2926. psychopy/tests/test_experiment/test_components/test_SettingsComponent.py +80 -0
  2927. psychopy/tests/test_experiment/test_components/test_StaticComponent.py +51 -0
  2928. psychopy/tests/test_experiment/test_components/test_UnknownPluginComponent.py +27 -0
  2929. psychopy/tests/test_experiment/test_components/test_all_components.py +104 -0
  2930. psychopy/tests/test_experiment/test_components/test_base_components.py +477 -0
  2931. psychopy/tests/test_experiment/test_experiment.py +199 -0
  2932. psychopy/tests/test_experiment/test_loops.py +103 -0
  2933. psychopy/tests/test_experiment/test_params.py +363 -0
  2934. psychopy/tests/test_experiment/test_py2js.py +230 -0
  2935. psychopy/tests/test_experiment/test_routines/__init__.py +1 -0
  2936. psychopy/tests/test_experiment/test_routines/test_EyetrackerCalibrationRoutine.py +9 -0
  2937. psychopy/tests/test_experiment/test_routines/test_PhotodiodeValidationRoutine.py +9 -0
  2938. psychopy/tests/test_experiment/test_routines/test_all_routines.py +23 -0
  2939. psychopy/tests/test_experiment/test_routines/test_base_routine.py +154 -0
  2940. psychopy/tests/test_experiment/test_routines/test_standalone_routines.py +36 -0
  2941. psychopy/tests/test_gui/test_DlgFromDictQt.py +82 -0
  2942. psychopy/tests/test_gui/test_DlgFromDictWx.py +82 -0
  2943. psychopy/tests/test_hardware/__init__.py +0 -0
  2944. psychopy/tests/test_hardware/test_device_manager.py +36 -0
  2945. psychopy/tests/test_hardware/test_emulator.py +106 -0
  2946. psychopy/tests/test_hardware/test_gammasci.py +35 -0
  2947. psychopy/tests/test_hardware/test_keyboard.py +241 -0
  2948. psychopy/tests/test_hardware/test_keyboard_events.py +362 -0
  2949. psychopy/tests/test_hardware/test_photodiode.py +66 -0
  2950. psychopy/tests/test_hardware/test_ports.py +129 -0
  2951. psychopy/tests/test_iohub/__init__.py +1 -0
  2952. psychopy/tests/test_iohub/test_computer.py +124 -0
  2953. psychopy/tests/test_iohub/test_event_get_and_clear.py +94 -0
  2954. psychopy/tests/test_iohub/test_keyboard.py +91 -0
  2955. psychopy/tests/test_iohub/test_launch.py +19 -0
  2956. psychopy/tests/test_iohub/testutil.py +54 -0
  2957. psychopy/tests/test_liaison/test_Liaison.py +275 -0
  2958. psychopy/tests/test_misc/__init__.py +0 -0
  2959. psychopy/tests/test_misc/memory_usage.py +164 -0
  2960. psychopy/tests/test_misc/test_GammaFun.py +15 -0
  2961. psychopy/tests/test_misc/test_clock.py +63 -0
  2962. psychopy/tests/test_misc/test_color.py +205 -0
  2963. psychopy/tests/test_misc/test_core.py +451 -0
  2964. psychopy/tests/test_misc/test_event.py +308 -0
  2965. psychopy/tests/test_misc/test_info.py +18 -0
  2966. psychopy/tests/test_misc/test_layout.py +49 -0
  2967. psychopy/tests/test_misc/test_locale.py +38 -0
  2968. psychopy/tests/test_misc/test_web.py +22 -0
  2969. psychopy/tests/test_monitors/test_monitors.py +91 -0
  2970. psychopy/tests/test_plugins/__init__.py +0 -0
  2971. psychopy/tests/test_plugins/test_plugin_stubs.py +125 -0
  2972. psychopy/tests/test_preferences/__init__.py +0 -0
  2973. psychopy/tests/test_preferences/test_prefs.py +28 -0
  2974. psychopy/tests/test_session/test_Session.py +257 -0
  2975. psychopy/tests/test_sound/__init__.py +0 -0
  2976. psychopy/tests/test_sound/test_audioclip.py +331 -0
  2977. psychopy/tests/test_sound/test_hamming.py +45 -0
  2978. psychopy/tests/test_sound/test_sound.py +138 -0
  2979. psychopy/tests/test_sound/test_sound_pygame.py +67 -0
  2980. psychopy/tests/test_tools/test_animationtools.py +51 -0
  2981. psychopy/tests/test_tools/test_arraytools.py +242 -0
  2982. psychopy/tests/test_tools/test_attributetools.py +257 -0
  2983. psychopy/tests/test_tools/test_colorspacetools.py +150 -0
  2984. psychopy/tests/test_tools/test_environmenttools.py +52 -0
  2985. psychopy/tests/test_tools/test_fileerrortools.py +97 -0
  2986. psychopy/tests/test_tools/test_filetools.py +104 -0
  2987. psychopy/tests/test_tools/test_imagetools.py +57 -0
  2988. psychopy/tests/test_tools/test_mathtools.py +688 -0
  2989. psychopy/tests/test_tools/test_stringtools.py +143 -0
  2990. psychopy/tests/test_tools/test_versionchooser.py +209 -0
  2991. psychopy/tests/test_tools/test_viewtools.py +168 -0
  2992. psychopy/tests/test_validators/__init__.py +0 -0
  2993. psychopy/tests/test_validators/test_voicekeyValidator.py +95 -0
  2994. psychopy/tests/test_visual/__init__.py +0 -0
  2995. psychopy/tests/test_visual/measure_parity.py +243 -0
  2996. psychopy/tests/test_visual/test_all_stimuli.py +655 -0
  2997. psychopy/tests/test_visual/test_basevisual.py +564 -0
  2998. psychopy/tests/test_visual/test_brush.py +68 -0
  2999. psychopy/tests/test_visual/test_button.py +15 -0
  3000. psychopy/tests/test_visual/test_circle.py +74 -0
  3001. psychopy/tests/test_visual/test_contains_overlaps.py +242 -0
  3002. psychopy/tests/test_visual/test_custommouse.py +47 -0
  3003. psychopy/tests/test_visual/test_dots.py +155 -0
  3004. psychopy/tests/test_visual/test_form.py +414 -0
  3005. psychopy/tests/test_visual/test_framepacking.py +41 -0
  3006. psychopy/tests/test_visual/test_gamma.py +152 -0
  3007. psychopy/tests/test_visual/test_glfw_backend.py +14 -0
  3008. psychopy/tests/test_visual/test_image.py +219 -0
  3009. psychopy/tests/test_visual/test_panorama.py +41 -0
  3010. psychopy/tests/test_visual/test_progress.py +96 -0
  3011. psychopy/tests/test_visual/test_projections.py +215 -0
  3012. psychopy/tests/test_visual/test_projections_interactive.py +163 -0
  3013. psychopy/tests/test_visual/test_roi.py +105 -0
  3014. psychopy/tests/test_visual/test_shape.py +23 -0
  3015. psychopy/tests/test_visual/test_slider.py +328 -0
  3016. psychopy/tests/test_visual/test_target.py +35 -0
  3017. psychopy/tests/test_visual/test_textbox.py +508 -0
  3018. psychopy/tests/test_visual/test_winFlipTiming.py +95 -0
  3019. psychopy/tests/test_visual/test_winScalePos.py +78 -0
  3020. psychopy/tests/test_visual/test_window.py +124 -0
  3021. psychopy/tests/utils.py +372 -0
  3022. psychopy/tools/LineBreak.txt +3597 -0
  3023. psychopy/tools/__init__.py +9 -0
  3024. psychopy/tools/animationtools.py +49 -0
  3025. psychopy/tools/apptools.py +32 -0
  3026. psychopy/tools/arraytools.py +561 -0
  3027. psychopy/tools/attributetools.py +263 -0
  3028. psychopy/tools/audiotools.py +361 -0
  3029. psychopy/tools/colorspacetools.py +717 -0
  3030. psychopy/tools/coordinatetools.py +104 -0
  3031. psychopy/tools/environmenttools.py +62 -0
  3032. psychopy/tools/fileerrortools.py +66 -0
  3033. psychopy/tools/filetools.py +406 -0
  3034. psychopy/tools/fontmanager.py +1065 -0
  3035. psychopy/tools/gltools.py +7367 -0
  3036. psychopy/tools/imagetools.py +65 -0
  3037. psychopy/tools/linebreak.py +322 -0
  3038. psychopy/tools/mathtools.py +4910 -0
  3039. psychopy/tools/monitorunittools.py +276 -0
  3040. psychopy/tools/movietools.py +1111 -0
  3041. psychopy/tools/pkgtools.py +654 -0
  3042. psychopy/tools/plottools.py +25 -0
  3043. psychopy/tools/rifttools.py +76 -0
  3044. psychopy/tools/stereotools.py +9 -0
  3045. psychopy/tools/stimulustools.py +198 -0
  3046. psychopy/tools/stringtools.py +466 -0
  3047. psychopy/tools/systemtools.py +1331 -0
  3048. psychopy/tools/typetools.py +52 -0
  3049. psychopy/tools/unittools.py +16 -0
  3050. psychopy/tools/versionchooser.py +604 -0
  3051. psychopy/tools/viewtools.py +1068 -0
  3052. psychopy/tools/wizard.py +804 -0
  3053. psychopy/validation/__init__.py +6 -0
  3054. psychopy/validation/audio.py +78 -0
  3055. psychopy/validation/visual.py +119 -0
  3056. psychopy/visual/__init__.py +121 -0
  3057. psychopy/visual/aperture.py +333 -0
  3058. psychopy/visual/backends/__init__.py +77 -0
  3059. psychopy/visual/backends/_base.py +481 -0
  3060. psychopy/visual/backends/gamma.py +349 -0
  3061. psychopy/visual/backends/glfwbackend.py +24 -0
  3062. psychopy/visual/backends/pygamebackend.py +343 -0
  3063. psychopy/visual/backends/pygletbackend.py +970 -0
  3064. psychopy/visual/basevisual.py +2027 -0
  3065. psychopy/visual/brush.py +204 -0
  3066. psychopy/visual/bufferimage.py +311 -0
  3067. psychopy/visual/button.py +194 -0
  3068. psychopy/visual/circle.py +165 -0
  3069. psychopy/visual/custommouse.py +263 -0
  3070. psychopy/visual/dot.py +734 -0
  3071. psychopy/visual/dropdown.py +165 -0
  3072. psychopy/visual/elementarray.py +802 -0
  3073. psychopy/visual/filters.py +419 -0
  3074. psychopy/visual/form.py +1195 -0
  3075. psychopy/visual/globalVars.py +24 -0
  3076. psychopy/visual/grating.py +581 -0
  3077. psychopy/visual/helpers.py +336 -0
  3078. psychopy/visual/image.py +438 -0
  3079. psychopy/visual/line.py +227 -0
  3080. psychopy/visual/movie.py +12 -0
  3081. psychopy/visual/movie2.py +21 -0
  3082. psychopy/visual/movie3.py +18 -0
  3083. psychopy/visual/movies/__init__.py +2334 -0
  3084. psychopy/visual/movies/frame.py +258 -0
  3085. psychopy/visual/movies/metadata.py +242 -0
  3086. psychopy/visual/movies/players/_base.py +364 -0
  3087. psychopy/visual/nnlvs.py +830 -0
  3088. psychopy/visual/noise.py +32 -0
  3089. psychopy/visual/panorama.py +313 -0
  3090. psychopy/visual/patch.py +21 -0
  3091. psychopy/visual/pie.py +237 -0
  3092. psychopy/visual/polygon.py +226 -0
  3093. psychopy/visual/progress.py +313 -0
  3094. psychopy/visual/radial.py +29 -0
  3095. psychopy/visual/ratingscale.py +18 -0
  3096. psychopy/visual/rect.py +195 -0
  3097. psychopy/visual/rift.py +2660 -0
  3098. psychopy/visual/roi.py +141 -0
  3099. psychopy/visual/secondorder.py +27 -0
  3100. psychopy/visual/shaders.py +787 -0
  3101. psychopy/visual/shape.py +897 -0
  3102. psychopy/visual/simpleimage.py +303 -0
  3103. psychopy/visual/slider.py +1222 -0
  3104. psychopy/visual/stim3d.py +2109 -0
  3105. psychopy/visual/target.py +278 -0
  3106. psychopy/visual/text.py +769 -0
  3107. psychopy/visual/textbox/__init__.py +1280 -0
  3108. psychopy/visual/textbox/fontmanager.py +574 -0
  3109. psychopy/visual/textbox/parsedtext.py +317 -0
  3110. psychopy/visual/textbox/textgrid.py +278 -0
  3111. psychopy/visual/textbox/textureatlas.py +248 -0
  3112. psychopy/visual/textbox2/__init__.py +4 -0
  3113. psychopy/visual/textbox2/textbox2.py +1970 -0
  3114. psychopy/visual/vlcmoviestim.py +1294 -0
  3115. psychopy/visual/window.py +3910 -0
  3116. psychopy/visual/windowframepack.py +86 -0
  3117. psychopy/visual/windowwarp.py +457 -0
  3118. psychopy/voicekey/__init__.py +56 -0
  3119. psychopy/voicekey/labjack_vks.py +9 -0
  3120. psychopy/voicekey/parallel_vks.py +9 -0
  3121. psychopy/voicekey/vk_tools.py +131 -0
  3122. psychopy/web.py +289 -0
  3123. psychopy-2025.2.4.dist-info/METADATA +160 -0
  3124. psychopy-2025.2.4.dist-info/RECORD +3127 -0
  3125. psychopy-2025.2.4.dist-info/WHEEL +4 -0
  3126. psychopy-2025.2.4.dist-info/entry_points.txt +5 -0
  3127. psychopy-2025.2.4.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,4910 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """Various math functions for working with vectors, matrices, and quaternions.
5
+ """
6
+
7
+ # Part of the PsychoPy library
8
+ # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2025 Open Science Tools Ltd.
9
+ # Distributed under the terms of the GNU General Public License (GPL).
10
+
11
+ __all__ = [
12
+ 'VEC_AXES', # constants
13
+ 'VEC_AXIS_FORWARD',
14
+ 'VEC_AXIS_BACKWARD',
15
+ 'VEC_AXIS_UP',
16
+ 'VEC_AXIS_DOWN',
17
+ 'VEC_AXIS_RIGHT',
18
+ 'VEC_AXIS_LEFT',
19
+ 'VEC_AXIS_POS_X',
20
+ 'VEC_AXIS_NEG_X',
21
+ 'VEC_AXIS_POS_Y',
22
+ 'VEC_AXIS_NEG_Y',
23
+ 'VEC_AXIS_POS_Z',
24
+ 'VEC_AXIS_NEG_Z',
25
+ 'RigidBodyPose', # rigid body pose class
26
+ 'BoundingBox',
27
+ 'length', # vector functions
28
+ 'normalize',
29
+ 'orthogonalize',
30
+ 'reflect',
31
+ 'dot',
32
+ 'cross',
33
+ 'project',
34
+ 'lerp',
35
+ 'distance',
36
+ 'perp',
37
+ 'bisector',
38
+ 'angleTo',
39
+ 'sortClockwise',
40
+ 'surfaceNormal',
41
+ 'surfaceBitangent',
42
+ 'surfaceTangent',
43
+ 'vertexNormal',
44
+ 'fixTangentHandedness',
45
+ 'fitBBox',
46
+ 'computeBBoxCorners',
47
+ 'intersectRayPlane',
48
+ 'intersectRaySphere',
49
+ 'intersectRayAABB',
50
+ 'intersectRayOBB',
51
+ 'intersectRayTriangle',
52
+ 'ortho3Dto2D',
53
+ 'articulate',
54
+ 'slerp', # quaternion functions
55
+ 'quatToAxisAngle',
56
+ 'quatFromAxisAngle',
57
+ 'quatYawPitchRoll',
58
+ 'quatMagnitude',
59
+ 'multQuat',
60
+ 'invertQuat',
61
+ 'applyQuat',
62
+ 'accumQuat',
63
+ 'alignTo',
64
+ 'matrixToQuat',
65
+ 'identityMatrix', # matrix functions
66
+ 'quatToMatrix',
67
+ 'scaleMatrix',
68
+ 'rotationMatrix',
69
+ 'translationMatrix',
70
+ 'invertMatrix',
71
+ 'multMatrix',
72
+ 'concatenate',
73
+ 'matrixFromEulerAngles',
74
+ 'isOrthogonal',
75
+ 'isAffine',
76
+ 'applyMatrix',
77
+ 'posOriToMatrix',
78
+ 'transform',
79
+ 'scale',
80
+ 'normalMatrix',
81
+ 'forwardProject',
82
+ 'reverseProject',
83
+ 'lookAt',
84
+ 'zeroFix', # misc functions
85
+ 'lensCorrection',
86
+ 'lensCorrectionSpherical',
87
+ 'infrange',
88
+ 'setDefaultPrecision'
89
+ ]
90
+
91
+
92
+ import numpy as np
93
+ import functools
94
+ import itertools
95
+
96
+ VEC_AXES = { # mapping of axis names to vectors
97
+ '+x': (1, 0, 0), '-x': (-1, 0, 0),
98
+ '+y': (0, 1, 0), '-y': (0, -1, 0),
99
+ '+z': (0, 0, 1), '-z': (0, 0, -1)}
100
+
101
+ # vectors for common axes
102
+ VEC_AXIS_BACKWARD = VEC_AXIS_POS_Z = VEC_AXES['+z']
103
+ VEC_AXIS_FORWARD = VEC_AXIS_NEG_Z = VEC_AXES['-z']
104
+ VEC_AXIS_UP = VEC_AXIS_POS_Y = VEC_AXES['+y']
105
+ VEC_AXIS_DOWN = VEC_AXIS_NEG_Y = VEC_AXES['-y']
106
+ VEC_AXIS_RIGHT = VEC_AXIS_POS_X = VEC_AXES['+x']
107
+ VEC_AXIS_LEFT = VEC_AXIS_NEG_X = VEC_AXES['-x']
108
+
109
+ DEFAULT_DTYPE = float
110
+
111
+
112
+ def setDefaultPrecision(dtype='float64'):
113
+ """Set the default precision for math functions.
114
+
115
+ Once set, all math functions in this module will use the specified data type
116
+ for computations in successive calls. This is useful when you want to ensure
117
+ a specific precision for all math operations.
118
+
119
+ Parameters
120
+ ----------
121
+ dtype : dtype or str
122
+ Data type for computations can either be 'float32' or 'float64'.
123
+
124
+ """
125
+ global DEFAULT_DTYPE
126
+ DEFAULT_DTYPE = np.dtype(dtype).type
127
+
128
+
129
+ # ------------------------------------------------------------------------------
130
+ # Classes for working with rigid body poses
131
+ #
132
+
133
+ class RigidBodyPose:
134
+ """Class for representing rigid body poses.
135
+
136
+ This class is an abstract representation of a rigid body pose, where the
137
+ position of the body in a scene is represented by a vector/coordinate and
138
+ the orientation with a quaternion. Pose can be manipulated and interacted
139
+ with using class methods and attributes. Rigid body poses assume a
140
+ right-handed coordinate system (-Z is forward and +Y is up).
141
+
142
+ Poses can be converted to 4x4 transformation matrices with `getModelMatrix`.
143
+ One can use these matrices when rendering to transform the vertices of a
144
+ model associated with the pose by passing them to OpenGL. Matrices are
145
+ cached internally to avoid recomputing them if `pos` and `ori` attributes
146
+ have not been updated.
147
+
148
+ Operators `*` and `~` can be used on `RigidBodyPose` objects to combine and
149
+ invert poses. For instance, you can multiply (`*`) poses to get a new pose
150
+ which is the combination of both orientations and translations by::
151
+
152
+ newPose = rb1 * rb2
153
+
154
+ Likewise, a pose can be inverted by using the `~` operator::
155
+
156
+ invPose = ~rb
157
+
158
+ Multiplying a pose by its inverse will result in an identity pose with no
159
+ translation and default orientation where `pos=[0, 0, 0]` and
160
+ `ori=[0, 0, 0, 1]`::
161
+
162
+ identityPose = ~rb * rb
163
+
164
+ Warnings
165
+ --------
166
+ This class is experimental and may result in undefined behavior.
167
+
168
+ """
169
+ def __init__(self, pos=(0., 0., 0.), ori=(0., 0., 0., 1.), dtype=None):
170
+ """
171
+ Parameters
172
+ ----------
173
+ pos : array_like
174
+ Position vector `[x, y, z]` for the origin of the rigid body.
175
+ ori : array_like
176
+ Orientation quaternion `[x, y, z, w]` where `x`, `y`, `z` are
177
+ imaginary and `w` is real.
178
+ dtype : dtype or str, optional
179
+ Data type for computations can either be 'float32' or 'float64'.
180
+ Default is `None` which uses the default data configured by
181
+ `setDefaultPrecision`.
182
+
183
+ """
184
+ # set the data type for computations
185
+ self._dtype = \
186
+ np.dtype(dtype).type if dtype is not None else DEFAULT_DTYPE
187
+
188
+ # position and orientation
189
+ self._pos = np.ascontiguousarray(pos, dtype=self.dtype)
190
+ self._ori = np.ascontiguousarray(ori, dtype=self.dtype)
191
+
192
+ self._modelMatrix = posOriToMatrix(
193
+ self._pos, self._ori, dtype=self.dtype)
194
+
195
+ # computed only if needed
196
+ self._normalMatrix = np.zeros((4, 4), dtype=self.dtype, order='C')
197
+ self._invModelMatrix = np.zeros((4, 4), dtype=self.dtype, order='C')
198
+ self._viewMatrix = np.zeros((4, 4), dtype=self.dtype, order='C')
199
+ self._invViewMatrix = np.zeros((4, 4), dtype=self.dtype, order='C')
200
+
201
+ # additional useful vectors
202
+ self._at = np.zeros((3,), dtype=self.dtype, order='C')
203
+ self._up = np.zeros((3,), dtype=self.dtype, order='C')
204
+ self._viewAxes = np.array( # cache for view matrix calculations
205
+ [VEC_AXIS_FORWARD, VEC_AXIS_UP], dtype=self.dtype, order='C')
206
+
207
+ # compute matrices only if `pos` and `ori` attributes have been updated,
208
+ # we track the state of the matrices with these flags
209
+ self._cacheFlags = {
210
+ 'model': True, # already computed
211
+ 'imodel': False,
212
+ 'normal': False,
213
+ 'view': False,
214
+ 'iview': False
215
+ }
216
+
217
+ self.pos = pos
218
+ self.ori = ori
219
+
220
+ self._bounds = None
221
+
222
+ def __repr__(self):
223
+ return 'RigidBodyPose(pos={}, ori={})'.format(self.pos, self.ori)
224
+
225
+ @property
226
+ def dtype(self):
227
+ """Data type used for computations and arrays (`numpy.dtype`).
228
+
229
+ Cannot be changed after object creation.
230
+
231
+ """
232
+ return self._dtype
233
+
234
+ @property
235
+ def bounds(self):
236
+ """Bounding box associated with this pose.
237
+ """
238
+ return self._bounds
239
+
240
+ @bounds.setter
241
+ def bounds(self, value):
242
+ self._bounds = value
243
+
244
+ @property
245
+ def pos(self):
246
+ """Position vector (X, Y, Z).
247
+ """
248
+ return self._pos
249
+
250
+ @pos.setter
251
+ def pos(self, value):
252
+ self._pos = np.ascontiguousarray(value, dtype=self.dtype)
253
+ self._cacheFlags = dict.fromkeys(self._cacheFlags.keys(), True)
254
+
255
+ @property
256
+ def ori(self):
257
+ """Orientation quaternion (X, Y, Z, W).
258
+ """
259
+ return self._ori
260
+
261
+ @ori.setter
262
+ def ori(self, value):
263
+ self._ori = np.ascontiguousarray(value, dtype=self.dtype)
264
+ self._cacheFlags = dict.fromkeys(self._cacheFlags.keys(), True)
265
+
266
+ @property
267
+ def posOri(self):
268
+ """The position (x, y, z) and orientation (x, y, z, w).
269
+ """
270
+ return self._pos, self._ori
271
+
272
+ @posOri.setter
273
+ def posOri(self, value):
274
+ self._pos = np.ascontiguousarray(value[0], dtype=self.dtype)
275
+ self._ori = np.ascontiguousarray(value[1], dtype=self.dtype)
276
+ self._cacheFlags = dict.fromkeys(self._cacheFlags.keys(), True)
277
+
278
+ @property
279
+ def at(self):
280
+ """Vector defining the forward direction (-Z) of this pose.
281
+ """
282
+ # matrix needs update, this need to be too
283
+ if self._cacheFlags['model']:
284
+ self._at = applyQuat(
285
+ self.ori, self._viewAxes[0, :], out=self._at, dtype=self.dtype)
286
+
287
+ return self._at
288
+
289
+ @property
290
+ def up(self):
291
+ """Vector defining the up direction (+Y) of this pose.
292
+ """
293
+ if self._cacheFlags['model']:
294
+ self._up = applyQuat(
295
+ self.ori, self._viewAxes[1, :], out=self._up, dtype=self.dtype)
296
+
297
+ return self._up
298
+
299
+ def __mul__(self, other):
300
+ """Multiply two poses, combining them to get a new pose.
301
+ """
302
+ newOri = multQuat(self._ori, other.ori)
303
+ return RigidBodyPose(transform(other.pos, newOri, self._pos), newOri)
304
+
305
+ def __imul__(self, other):
306
+ """Inplace multiplication. Transforms this pose by another.
307
+ """
308
+ self._ori = multQuat(self._ori, other.ori)
309
+ self._pos = transform(other.pos, self._ori, self._pos)
310
+
311
+ def copy(self):
312
+ """Get a new `RigidBodyPose` object which copies the position and
313
+ orientation of this one. Copies are independent and do not reference
314
+ each others data.
315
+
316
+ Returns
317
+ -------
318
+ RigidBodyPose
319
+ Copy of this pose.
320
+
321
+ """
322
+ return RigidBodyPose(self._pos, self._ori)
323
+
324
+ def isEqual(self, other):
325
+ """Check if poses have similar orientation and position.
326
+
327
+ Parameters
328
+ ----------
329
+ other : `RigidBodyPose`
330
+ Other pose to compare.
331
+
332
+ Returns
333
+ -------
334
+ bool
335
+ Returns `True` is poses are effectively equal.
336
+
337
+ """
338
+ return np.isclose(self._pos, other.pos) and \
339
+ np.isclose(self._ori, other.ori)
340
+
341
+ def clear(self):
342
+ """Clear the pose, setting position and orientation to zero.
343
+ """
344
+ self._pos.fill(0.0)
345
+ self._ori[:3] = 0.0
346
+ self._ori[3] = 1.0
347
+ self._cacheFlags = dict.fromkeys(a.keys(), True)
348
+
349
+ def setIdentity(self):
350
+ """Clear rigid body transformations (alias for `clear`).
351
+ """
352
+ self.clear()
353
+
354
+ def getOriAxisAngle(self, degrees=True):
355
+ """Get the axis and angle of rotation for the rigid body. Converts the
356
+ orientation defined by the `ori` quaternion to and axis-angle
357
+ representation.
358
+
359
+ Parameters
360
+ ----------
361
+ degrees : bool, optional
362
+ Specify ``True`` if `angle` is in degrees, or else it will be
363
+ treated as radians. Default is ``True``.
364
+
365
+ Returns
366
+ -------
367
+ tuple
368
+ Axis [rx, ry, rz] and angle.
369
+
370
+ """
371
+ return quatToAxisAngle(self._ori, degrees, dtype=self.dtype)
372
+
373
+ def setOriAxisAngle(self, axis, angle, degrees=True):
374
+ """Set the orientation of the rigid body using an `axis` and
375
+ `angle`. This sets the quaternion at `ori`.
376
+
377
+ Parameters
378
+ ----------
379
+ axis : array_like
380
+ Axis of rotation [rx, ry, rz].
381
+ angle : float
382
+ Angle of rotation.
383
+ degrees : bool, optional
384
+ Specify ``True`` if `angle` is in degrees, or else it will be
385
+ treated as radians. Default is ``True``.
386
+
387
+ """
388
+ self.ori = quatFromAxisAngle(axis, angle, degrees, dtype=self.dtype)
389
+
390
+ def getYawPitchRoll(self, degrees=True):
391
+ """Get the yaw, pitch and roll angles for this pose relative to the -Z
392
+ world axis.
393
+
394
+ Parameters
395
+ ----------
396
+ degrees : bool, optional
397
+ Specify ``True`` if `angle` is in degrees, or else it will be
398
+ treated as radians. Default is ``True``.
399
+
400
+ """
401
+ return quatYawPitchRoll(self._ori, degrees, dtype=self.dtype)
402
+
403
+ @property
404
+ def modelMatrix(self):
405
+ """Pose as a 4x4 model matrix (read-only)."""
406
+ if not self._cacheFlags['model']:
407
+ return self._modelMatrix
408
+ else:
409
+ return self.getModelMatrix()
410
+
411
+ @property
412
+ def inverseModelMatrix(self):
413
+ """Inverse of the pose as a 4x4 model matrix (read-only)."""
414
+ if not self._cacheFlags['imodel']:
415
+ return self._invModelMatrix
416
+ else:
417
+ return self.getModelMatrix(inverse=True)
418
+
419
+ @property
420
+ def normalMatrix(self):
421
+ """The 4x4 normal transformation matrix (read-only)."""
422
+ if not self._cacheFlags['normal']:
423
+ return self._normalMatrix
424
+ else:
425
+ return self.getNormalMatrix()
426
+
427
+ @property
428
+ def viewMatrix(self):
429
+ """The 4x4 view matrix for this pose (read-only)."""
430
+ if not self._cacheFlags['view']:
431
+ return self._viewMatrix
432
+ else:
433
+ return self.getViewMatrix()
434
+
435
+ @property
436
+ def inverseViewMatrix(self):
437
+ """The inverse of the 4x4 view matrix for this pose (read-only)."""
438
+ if not self._cacheFlags['iview']:
439
+ return self._invViewMatrix
440
+ else:
441
+ return self.getViewMatrix(inverse=True)
442
+
443
+ def getNormalMatrix(self, out=None):
444
+ """Get the present normal matrix.
445
+
446
+ Parameters
447
+ ----------
448
+ out : ndarray or None
449
+ Optional 4x4 array to write values to. Values written are computed
450
+ using 32-bit float precision regardless of the data type of `out`.
451
+
452
+ Returns
453
+ -------
454
+ ndarray
455
+ 4x4 normal transformation matrix.
456
+
457
+ """
458
+ if not self._cacheFlags['normal']:
459
+ return self._normalMatrix
460
+
461
+ self._normalMatrix[:, :] = np.linalg.inv(self.modelMatrix).T
462
+ self._cacheFlags['normal'] = False
463
+
464
+ if out is not None:
465
+ out[:, :] = self._normalMatrix[:, :]
466
+ return out
467
+
468
+ return self._normalMatrix
469
+
470
+ def getModelMatrix(self, inverse=False, out=None):
471
+ """Get the present rigid body transformation as a 4x4 matrix.
472
+
473
+ Matrices are computed only if the `pos` and `ori` attributes have been
474
+ updated since the last call to `getModelMatrix`. The returned matrix is
475
+ an `ndarray` and row-major.
476
+
477
+ Parameters
478
+ ----------
479
+ inverse : bool, optional
480
+ Return the inverse of the model matrix.
481
+ out : ndarray or None
482
+ Optional 4x4 array to write values to. Values written are computed
483
+ using 32-bit float precision regardless of the data type of `out`.
484
+
485
+ Returns
486
+ -------
487
+ ndarray
488
+ 4x4 transformation matrix.
489
+
490
+ Examples
491
+ --------
492
+ Using a rigid body pose to transform something in OpenGL::
493
+
494
+ rb = RigidBodyPose((0, 0, -2)) # 2 meters away from origin
495
+
496
+ # Use `array2pointer` from `psychopy.tools.arraytools` to convert
497
+ # array to something OpenGL accepts.
498
+ mv = array2pointer(rb.modelMatrix)
499
+
500
+ # use the matrix to transform the scene
501
+ glMatrixMode(GL_MODELVIEW)
502
+ glPushMatrix()
503
+ glLoadIdentity()
504
+ glMultTransposeMatrixf(mv)
505
+
506
+ # draw the thing here ...
507
+
508
+ glPopMatrix()
509
+
510
+ """
511
+ if self._cacheFlags['model']:
512
+ self._modelMatrix = posOriToMatrix(
513
+ self._pos, self._ori,
514
+ out=self._modelMatrix,
515
+ dtype=self.dtype)
516
+
517
+ # all other matrices need update when next accessed
518
+ self._cacheFlags['model'] = False
519
+ self._cacheFlags['imodel'] = True
520
+ self._cacheFlags['normal'] = True
521
+ self._cacheFlags['view'] = True
522
+ self._cacheFlags['iview'] = True
523
+
524
+ if not inverse:
525
+ toReturn = self._modelMatrix
526
+ else:
527
+ if self._cacheFlags['imodel']:
528
+ self._invModelMatrix = invertMatrix(
529
+ self._modelMatrix, out=self._invModelMatrix,
530
+ dtype=self.dtype)
531
+ self._cacheFlags['imodel'] = False
532
+
533
+ toReturn = self._invModelMatrix
534
+
535
+ if out is not None:
536
+ out[:, :] = toReturn[:, :]
537
+ return out
538
+
539
+ return toReturn
540
+
541
+ def getViewMatrix(self, inverse=False, out=None):
542
+ """Convert this pose into a view matrix.
543
+
544
+ Creates a view matrix which transforms points into eye space using the
545
+ current pose as the eye position in the scene. Furthermore, you can use
546
+ view matrices for rendering shadows if light positions are defined
547
+ as `RigidBodyPose` objects.
548
+
549
+ Parameters
550
+ ----------
551
+ inverse : bool
552
+ Return the inverse of the view matrix. Default is `False`.
553
+ out : ndarray or None
554
+ Optional 4x4 array to write values to. Values written are computed
555
+ using 32-bit float precision regardless of the data type of `out`.
556
+
557
+ Returns
558
+ -------
559
+ ndarray
560
+ 4x4 transformation matrix.
561
+
562
+ """
563
+ if self._cacheFlags['view']: # needs update?
564
+ # compute the view matrix
565
+ rotMatrix = quatToMatrix(self._ori, dtype=self.dtype)
566
+ transformedAxes = applyMatrix(
567
+ rotMatrix, self._viewAxes,
568
+ dtype=self.dtype)
569
+
570
+ fwdVec = transformedAxes[0, :] + self._pos
571
+ upVec = transformedAxes[1, :]
572
+
573
+ self._viewMatrix = lookAt(
574
+ self._pos, fwdVec, upVec,
575
+ out=self._viewMatrix,
576
+ dtype=self.dtype)
577
+
578
+ self._cacheFlags['view'] = False
579
+ self._cacheFlags['iview'] = True # inverse needs update
580
+
581
+ if not inverse:
582
+ toReturn = self._viewMatrix
583
+ else:
584
+ if self._cacheFlags['iview']:
585
+ self._invViewMatrix = invertMatrix(
586
+ self._viewMatrix,
587
+ out=self._invViewMatrix,
588
+ dtype=self.dtype)
589
+ self._cacheFlags['iview'] = False
590
+
591
+ toReturn = self._invViewMatrix
592
+
593
+ if out is not None:
594
+ out[:, :] = toReturn[:, :]
595
+ return out
596
+
597
+ return toReturn
598
+
599
+ def transform(self, v, out=None):
600
+ """Transform a vector using this pose.
601
+
602
+ Parameters
603
+ ----------
604
+ v : array_like
605
+ Vector to transform [x, y, z].
606
+ out : ndarray or None, optional
607
+ Optional array to write values to. Must have the same shape as
608
+ `v`.
609
+
610
+ Returns
611
+ -------
612
+ ndarray
613
+ Transformed points.
614
+
615
+ """
616
+ return transform(
617
+ self._pos, self._ori, points=v, out=out, dtype=self.dtype)
618
+
619
+ def transformNormal(self, n):
620
+ """Rotate a normal vector with respect to this pose.
621
+
622
+ Rotates a normal vector `n` using the orientation quaternion at `ori`.
623
+
624
+ Parameters
625
+ ----------
626
+ n : array_like
627
+ Normal to rotate (1-D with length 3).
628
+
629
+ Returns
630
+ -------
631
+ ndarray
632
+ Rotated normal `n`.
633
+
634
+ """
635
+ pout = np.zeros((3,), dtype=self.dtype)
636
+ pout[:] = n
637
+ t = np.cross(self._ori[:3], n[:3]) * 2.0
638
+ u = np.cross(self._ori[:3], t)
639
+ t *= self._ori[3]
640
+ pout[:3] += t
641
+ pout[:3] += u
642
+
643
+ return pout
644
+
645
+ def __invert__(self):
646
+ """Operator `~` to invert the pose. Returns a `RigidBodyPose` object.
647
+ """
648
+ return RigidBodyPose(
649
+ -self._pos, invertQuat(self._ori, dtype=self.dtype))
650
+
651
+ def invert(self):
652
+ """Invert this pose.
653
+ """
654
+ self._ori = invertQuat(self._ori, dtype=self.dtype)
655
+ self._pos *= -1.0
656
+
657
+ def inverted(self):
658
+ """Get a pose which is the inverse of this one.
659
+
660
+ Returns
661
+ -------
662
+ RigidBodyPose
663
+ This pose inverted.
664
+
665
+ """
666
+ return RigidBodyPose(
667
+ -self._pos, invertQuat(self._ori, dtype=self.dtype))
668
+
669
+ def distanceTo(self, v):
670
+ """Get the distance to a pose or point in scene units.
671
+
672
+ Parameters
673
+ ----------
674
+ v : RigidBodyPose or array_like
675
+ Pose or point [x, y, z] to compute distance to.
676
+
677
+ Returns
678
+ -------
679
+ float
680
+ Distance to `v` from this pose's origin.
681
+
682
+ """
683
+ if hasattr(v, 'pos'): # v is pose-like object
684
+ targetPos = v.pos
685
+ else:
686
+ targetPos = np.asarray(v[:3])
687
+
688
+ return np.sqrt(np.sum(np.square(targetPos - self.pos)))
689
+
690
+ def interp(self, end, s):
691
+ """Interpolate between poses.
692
+
693
+ Linear interpolation is used on position (Lerp) while the orientation
694
+ has spherical linear interpolation (Slerp) applied taking the shortest
695
+ arc on the hypersphere.
696
+
697
+ Parameters
698
+ ----------
699
+ end : RigidBodyPose
700
+ End pose.
701
+ s : float
702
+ Interpolation factor between interval 0.0 and 1.0.
703
+
704
+ Returns
705
+ -------
706
+ RigidBodyPose
707
+ Rigid body pose whose position and orientation is at `s` between
708
+ this pose and `end`.
709
+
710
+ """
711
+ if not (hasattr(end, 'pos') and hasattr(end, 'ori')):
712
+ raise TypeError("Object for `end` does not have attributes "
713
+ "`pos` and `ori`.")
714
+
715
+ interpPos = lerp(self._pos, end.pos, s, dtype=self.dtype)
716
+ interpOri = slerp(self._ori, end.ori, s, dtype=self.dtype)
717
+
718
+ return RigidBodyPose(interpPos, interpOri)
719
+
720
+ def alignTo(self, alignTo):
721
+ """Align this pose to another point or pose.
722
+
723
+ This sets the orientation of this pose to one which orients the forward
724
+ axis towards `alignTo`.
725
+
726
+ Parameters
727
+ ----------
728
+ alignTo : array_like or RigidBodyPose
729
+ Position vector [x, y, z] or pose to align to.
730
+
731
+ """
732
+ if hasattr(alignTo, 'pos'): # v is pose-like object
733
+ targetPos = alignTo.pos
734
+ else:
735
+ targetPos = np.asarray(alignTo[:3]) # handle array_like
736
+
737
+ fwd = np.asarray([0, 0, -1], dtype=self.dtype)
738
+ toTarget = targetPos - self._pos
739
+ invOri = invertQuat(self._ori, dtype=self.dtype)
740
+ invPos = applyQuat(invOri, toTarget, dtype=self.dtype)
741
+ invPos = normalize(invPos, dtype=self.dtype)
742
+
743
+ self.ori = multQuat(
744
+ self._ori, alignTo(fwd, invPos, dtype=self.dtype))
745
+
746
+
747
+
748
+ class BoundingBox:
749
+ """Class for representing object bounding boxes.
750
+
751
+ A bounding box is a construct which represents a 3D rectangular volume about
752
+ some pose, defined by its minimum and maximum extents in the reference frame
753
+ of the pose. The axes of the bounding box are aligned to the axes of the
754
+ world or the associated pose.
755
+
756
+ Bounding boxes are primarily used for visibility testing; to determine if
757
+ the extents of an object associated with a pose (eg. the vertices of a
758
+ model) falls completely outside of the viewing frustum. If so, the model can
759
+ be culled during rendering to avoid wasting CPU/GPU resources on objects not
760
+ visible to the viewer.
761
+
762
+ """
763
+ def __init__(self, extents=None, dtype=None):
764
+ self._dtype = np.dtype(dtype).type if dtype is not None else DEFAULT_DTYPE
765
+
766
+ self._extents = np.zeros((2, 3), self.dtype)
767
+ self._posCorners = np.zeros((8, 4), self.dtype)
768
+
769
+ if extents is not None:
770
+ self._extents[0, :] = extents[0]
771
+ self._extents[1, :] = extents[1]
772
+ else:
773
+ self.clear()
774
+
775
+ self._computeCorners()
776
+
777
+ def _computeCorners(self):
778
+ """Compute the corners of the bounding box.
779
+
780
+ These values are cached to speed up computations if extents hasn't been
781
+ updated.
782
+
783
+ """
784
+ for i in range(8):
785
+ self._posCorners[i, 0] = \
786
+ self._extents[1, 0] if (i & 1) else self._extents[0, 0]
787
+ self._posCorners[i, 1] = \
788
+ self._extents[1, 1] if (i & 2) else self._extents[0, 1]
789
+ self._posCorners[i, 2] = \
790
+ self._extents[1, 2] if (i & 4) else self._extents[0, 2]
791
+ self._posCorners[i, 3] = 1.0
792
+
793
+ @property
794
+ def dtype(self):
795
+ """Data type used for computations and arrays (`numpy.dtype`).
796
+ """
797
+ # we use 32-bit float precision for all computations, this will be
798
+ # settable in the future
799
+ return self._dtype
800
+
801
+ @property
802
+ def isValid(self):
803
+ """`True` if the bounding box is valid."""
804
+ return np.all(self._extents[0, :] <= self._extents[1, :])
805
+
806
+ @property
807
+ def extents(self):
808
+ return self._extents
809
+
810
+ @extents.setter
811
+ def extents(self, value):
812
+ self._extents[0, :] = value[0]
813
+ self._extents[1, :] = value[1]
814
+ self._computeCorners()
815
+
816
+ def fit(self, verts):
817
+ """Fit the bounding box to vertices."""
818
+ np.amin(verts, axis=0, out=self._extents[0])
819
+ np.amax(verts, axis=0, out=self._extents[1])
820
+ self._computeCorners()
821
+
822
+ def clear(self):
823
+ """Clear a bounding box, invalidating it."""
824
+ self._extents[0, :] = np.finfo(self.dtype).max
825
+ self._extents[1, :] = np.finfo(self.dtype).min
826
+ self._computeCorners()
827
+
828
+
829
+ # ------------------------------------------------------------------------------
830
+ # Vector Operations
831
+ #
832
+
833
+ def length(v, squared=False, out=None, dtype=None):
834
+ """Get the length of a vector.
835
+
836
+ Parameters
837
+ ----------
838
+ v : array_like
839
+ Vector to normalize, can be Nx2, Nx3, or Nx4. If a 2D array is
840
+ specified, rows are treated as separate vectors.
841
+ squared : bool, optional
842
+ If ``True`` the squared length is returned. The default is ``False``.
843
+ out : ndarray, optional
844
+ Optional output array. Must be same `shape` and `dtype` as the expected
845
+ output if `out` was not specified.
846
+ dtype : dtype or str, optional
847
+ Data type for computations can either be 'float32' or 'float64'. If
848
+ `out` is specified, the data type of `out` is used and this argument is
849
+ ignored. If `out` is not provided, 'float64' is used by default.
850
+
851
+ Returns
852
+ -------
853
+ float or ndarray
854
+ Length of vector `v`.
855
+
856
+ """
857
+ if out is None:
858
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
859
+ toReturn = np.array(v, dtype=dtype)
860
+ else:
861
+ toReturn = out
862
+
863
+ v = np.asarray(v, dtype=dtype)
864
+
865
+ if v.ndim == 2:
866
+ assert v.shape[1] <= 4
867
+ toReturn = np.zeros((v.shape[0],), dtype=dtype) if out is None else out
868
+ v2d, vr = np.atleast_2d(v, toReturn) # 2d view of array
869
+ if squared:
870
+ vr[:, :] = np.sum(np.square(v2d), axis=1)
871
+ else:
872
+ vr[:, :] = np.sqrt(np.sum(np.square(v2d), axis=1))
873
+ elif v.ndim == 1:
874
+ assert v.shape[0] <= 4
875
+ if squared:
876
+ toReturn = np.sum(np.square(v))
877
+ else:
878
+ toReturn = np.sqrt(np.sum(np.square(v)))
879
+ else:
880
+ raise ValueError("Input arguments have invalid dimensions.")
881
+
882
+ return toReturn
883
+
884
+
885
+ def normalize(v, out=None, dtype=None):
886
+ """Normalize a vector or quaternion.
887
+
888
+ v : array_like
889
+ Vector to normalize, can be Nx2, Nx3, or Nx4. If a 2D array is
890
+ specified, rows are treated as separate vectors. All vectors should have
891
+ nonzero length.
892
+ out : ndarray, optional
893
+ Optional output array. Must be same `shape` and `dtype` as the expected
894
+ output if `out` was not specified.
895
+ dtype : dtype or str, optional
896
+ Data type for computations can either be 'float32' or 'float64'. If
897
+ `out` is specified, the data type of `out` is used and this argument is
898
+ ignored. If `out` is not provided, 'float64' is used by default.
899
+
900
+ Returns
901
+ -------
902
+ ndarray
903
+ Normalized vector `v`.
904
+
905
+ Notes
906
+ -----
907
+ * If the vector has length is zero, a vector of all zeros is returned after
908
+ normalization.
909
+
910
+ Examples
911
+ --------
912
+ Normalize a vector::
913
+
914
+ v = [1., 2., 3., 4.]
915
+ vn = normalize(v)
916
+
917
+ The `normalize` function is vectorized. It's considerably faster to
918
+ normalize large arrays of vectors than to call `normalize` separately for
919
+ each one::
920
+
921
+ v = np.random.uniform(-1.0, 1.0, (1000, 4,)) # 1000 length 4 vectors
922
+ vn = np.zeros((1000, 4)) # place to write values
923
+ normalize(v, out=vn) # very fast!
924
+
925
+ # don't do this!
926
+ for i in range(1000):
927
+ vn[i, :] = normalize(v[i, :])
928
+
929
+ """
930
+ if out is None:
931
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
932
+ toReturn = np.array(v, dtype=dtype)
933
+ else:
934
+ toReturn = out
935
+
936
+ v2d = np.atleast_2d(toReturn) # 2d view of array
937
+ norm = np.linalg.norm(v2d, axis=1)
938
+ norm[norm == 0.0] = np.nan # make sure if length==0 division succeeds
939
+ v2d /= norm[:, np.newaxis]
940
+ np.nan_to_num(v2d, copy=False) # fix NaNs
941
+
942
+ return toReturn
943
+
944
+
945
+ def orthogonalize(v, n, out=None, dtype=None):
946
+ """Orthogonalize a vector relative to a normal vector.
947
+
948
+ This function ensures that `v` is perpendicular (or orthogonal) to `n`.
949
+
950
+ Parameters
951
+ ----------
952
+ v : array_like
953
+ Vector to orthogonalize, can be Nx2, Nx3, or Nx4. If a 2D array is
954
+ specified, rows are treated as separate vectors.
955
+ n : array_like
956
+ Normal vector, must have same shape as `v`.
957
+ out : ndarray, optional
958
+ Optional output array. Must be same `shape` and `dtype` as the expected
959
+ output if `out` was not specified.
960
+ dtype : dtype or str, optional
961
+ Data type for computations can either be 'float32' or 'float64'. If
962
+ `out` is specified, the data type of `out` is used and this argument is
963
+ ignored. If `out` is not provided, 'float64' is used by default.
964
+
965
+ Returns
966
+ -------
967
+ ndarray
968
+ Orthogonalized vector `v` relative to normal vector `n`.
969
+
970
+ Warnings
971
+ --------
972
+ If `v` and `n` are the same, the direction of the perpendicular vector is
973
+ indeterminate. The resulting vector is degenerate (all zeros).
974
+
975
+ """
976
+ if out is None:
977
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
978
+ else:
979
+ dtype = np.dtype(out.dtype).type
980
+
981
+ v = np.asarray(v, dtype=dtype)
982
+ n = np.asarray(n, dtype=dtype)
983
+
984
+ if out is None:
985
+ toReturn = np.zeros_like(v, dtype=dtype)
986
+ else:
987
+ toReturn = out
988
+ toReturn.fill(0.0)
989
+
990
+ v, n, vr = np.atleast_2d(v, n, toReturn)
991
+ vr[:, :] = v
992
+ vr[:, :] -= n * np.sum(n * v, axis=1)[:, np.newaxis] # dot product
993
+ normalize(vr, out=vr)
994
+
995
+ return toReturn
996
+
997
+
998
+ def reflect(v, n, out=None, dtype=None):
999
+ """Reflection of a vector.
1000
+
1001
+ Get the reflection of `v` relative to normal `n`.
1002
+
1003
+ Parameters
1004
+ ----------
1005
+ v : array_like
1006
+ Vector to reflect, can be Nx2, Nx3, or Nx4. If a 2D array is specified,
1007
+ rows are treated as separate vectors.
1008
+ n : array_like
1009
+ Normal vector, must have same shape as `v`.
1010
+ out : ndarray, optional
1011
+ Optional output array. Must be same `shape` and `dtype` as the expected
1012
+ output if `out` was not specified.
1013
+ dtype : dtype or str, optional
1014
+ Data type for computations can either be 'float32' or 'float64'. If
1015
+ `out` is specified, the data type of `out` is used and this argument is
1016
+ ignored. If `out` is not provided, 'float64' is used by default.
1017
+
1018
+ Returns
1019
+ -------
1020
+ ndarray
1021
+ Reflected vector `v` off normal `n`.
1022
+
1023
+ """
1024
+ # based off https://github.com/glfw/glfw/blob/master/deps/linmath.h
1025
+ if out is None:
1026
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1027
+ else:
1028
+ dtype = np.dtype(out.dtype).type
1029
+
1030
+ v = np.asarray(v, dtype=dtype)
1031
+ n = np.asarray(n, dtype=dtype)
1032
+
1033
+ if out is None:
1034
+ toReturn = np.zeros_like(v, dtype=dtype)
1035
+ else:
1036
+ toReturn = out
1037
+ toReturn.fill(0.0)
1038
+
1039
+ v, n, vr = np.atleast_2d(v, n, toReturn)
1040
+
1041
+ vr[:, :] = v
1042
+ vr[:, :] -= (dtype(2.0) * np.sum(n * v, axis=1))[:, np.newaxis] * n
1043
+
1044
+ return toReturn
1045
+
1046
+
1047
+ def dot(v0, v1, out=None, dtype=None):
1048
+ """Dot product of two vectors.
1049
+
1050
+ The behaviour of this function depends on the format of the input arguments:
1051
+
1052
+ * If `v0` and `v1` are 1D, the dot product is returned as a scalar and `out`
1053
+ is ignored.
1054
+ * If `v0` and `v1` are 2D, a 1D array of dot products between corresponding
1055
+ row vectors are returned.
1056
+ * If either `v0` and `v1` are 1D and 2D, an array of dot products
1057
+ between each row of the 2D vector and the 1D vector are returned.
1058
+
1059
+ Parameters
1060
+ ----------
1061
+ v0, v1 : array_like
1062
+ Vector(s) to compute dot products of (e.g. [x, y, z]). `v0` must have
1063
+ equal or fewer dimensions than `v1`.
1064
+ out : ndarray, optional
1065
+ Optional output array. Must be same `shape` and `dtype` as the expected
1066
+ output if `out` was not specified.
1067
+ dtype : dtype or str, optional
1068
+ Data type for computations can either be 'float32' or 'float64'. If
1069
+ `out` is specified, the data type of `out` is used and this argument is
1070
+ ignored. If `out` is not provided, 'float64' is used by default.
1071
+
1072
+ Returns
1073
+ -------
1074
+ ndarray
1075
+ Dot product(s) of `v0` and `v1`.
1076
+
1077
+ """
1078
+ if out is None:
1079
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1080
+ else:
1081
+ dtype = np.dtype(out.dtype).type
1082
+
1083
+ v0 = np.asarray(v0, dtype=dtype)
1084
+ v1 = np.asarray(v1, dtype=dtype)
1085
+
1086
+ if v0.ndim == v1.ndim == 2 or v0.ndim == 2 and v1.ndim == 1:
1087
+ toReturn = np.zeros((v0.shape[0],), dtype=dtype) if out is None else out
1088
+ vr = np.atleast_2d(toReturn) # make sure we have a 2d view
1089
+ vr[:] = np.sum(v1 * v0, axis=1)
1090
+ elif v0.ndim == v1.ndim == 1:
1091
+ toReturn = np.sum(v1 * v0)
1092
+ elif v0.ndim == 1 and v1.ndim == 2:
1093
+ toReturn = np.zeros((v1.shape[0],), dtype=dtype) if out is None else out
1094
+ vr = np.atleast_2d(toReturn) # make sure we have a 2d view
1095
+ vr[:] = np.sum(v1 * v0, axis=1)
1096
+ else:
1097
+ raise ValueError("Input arguments have invalid dimensions.")
1098
+
1099
+ return toReturn
1100
+
1101
+
1102
+ def cross(v0, v1, out=None, dtype=None):
1103
+ """Cross product of 3D vectors.
1104
+
1105
+ The behavior of this function depends on the dimensions of the inputs:
1106
+
1107
+ * If `v0` and `v1` are 1D, the cross product is returned as 1D vector.
1108
+ * If `v0` and `v1` are 2D, a 2D array of cross products between
1109
+ corresponding row vectors are returned.
1110
+ * If either `v0` and `v1` are 1D and 2D, an array of cross products
1111
+ between each row of the 2D vector and the 1D vector are returned.
1112
+
1113
+ Parameters
1114
+ ----------
1115
+ v0, v1 : array_like
1116
+ Vector(s) in form [x, y, z] or [x, y, z, 1].
1117
+ out : ndarray, optional
1118
+ Optional output array. Must be same `shape` and `dtype` as the expected
1119
+ output if `out` was not specified.
1120
+ dtype : dtype or str, optional
1121
+ Data type for computations can either be 'float32' or 'float64'. If
1122
+ `out` is specified, the data type of `out` is used and this argument is
1123
+ ignored. If `out` is not provided, 'float64' is used by default.
1124
+
1125
+ Returns
1126
+ -------
1127
+ ndarray
1128
+ Cross product of `v0` and `v1`.
1129
+
1130
+ Notes
1131
+ -----
1132
+ * If input vectors are 4D, the last value of cross product vectors is always
1133
+ set to one.
1134
+ * If input vectors `v0` and `v1` are Nx3 and `out` is Nx4, the cross product
1135
+ is computed and the last column of `out` is filled with ones.
1136
+
1137
+ Examples
1138
+ --------
1139
+
1140
+ Find the cross product of two vectors::
1141
+
1142
+ a = normalize([1, 2, 3])
1143
+ b = normalize([3, 2, 1])
1144
+ c = cross(a, b)
1145
+
1146
+ If input arguments are 2D, the function returns the cross products of
1147
+ corresponding rows::
1148
+
1149
+ # create two 6x3 arrays with random numbers
1150
+ shape = (6, 3,)
1151
+ a = normalize(np.random.uniform(-1.0, 1.0, shape))
1152
+ b = normalize(np.random.uniform(-1.0, 1.0, shape))
1153
+ cprod = np.zeros(shape) # output has the same shape as inputs
1154
+ cross(a, b, out=cprod)
1155
+
1156
+ If a 1D and 2D vector are specified, the cross product of each row of the
1157
+ 2D array and the 1D array is returned as a 2D array::
1158
+
1159
+ a = normalize([1, 2, 3])
1160
+ b = normalize(np.random.uniform(-1.0, 1.0, (6, 3,)))
1161
+ cprod = np.zeros(a.shape)
1162
+ cross(a, b, out=cprod)
1163
+
1164
+ """
1165
+ if out is None:
1166
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1167
+ else:
1168
+ dtype = np.dtype(out.dtype).type
1169
+
1170
+ v0 = np.asarray(v0, dtype=dtype)
1171
+ v1 = np.asarray(v1, dtype=dtype)
1172
+
1173
+ if v0.ndim == v1.ndim == 2: # 2D x 2D
1174
+ assert v0.shape == v1.shape
1175
+ toReturn = np.zeros(v0.shape, dtype=dtype) if out is None else out
1176
+ vr = np.atleast_2d(toReturn)
1177
+ vr[:, 0] = v0[:, 1] * v1[:, 2] - v0[:, 2] * v1[:, 1]
1178
+ vr[:, 1] = v0[:, 2] * v1[:, 0] - v0[:, 0] * v1[:, 2]
1179
+ vr[:, 2] = v0[:, 0] * v1[:, 1] - v0[:, 1] * v1[:, 0]
1180
+
1181
+ if vr.shape[1] == 4:
1182
+ vr[:, 3] = dtype(1.0)
1183
+
1184
+ elif v0.ndim == v1.ndim == 1: # 1D x 1D
1185
+ assert v0.shape == v1.shape
1186
+ toReturn = np.zeros(v0.shape, dtype=dtype) if out is None else out
1187
+ toReturn[0] = v0[1] * v1[2] - v0[2] * v1[1]
1188
+ toReturn[1] = v0[2] * v1[0] - v0[0] * v1[2]
1189
+ toReturn[2] = v0[0] * v1[1] - v0[1] * v1[0]
1190
+
1191
+ if toReturn.shape[0] == 4:
1192
+ toReturn[3] = dtype(1.0)
1193
+
1194
+ elif v0.ndim == 2 and v1.ndim == 1: # 2D x 1D
1195
+ toReturn = np.zeros(v0.shape, dtype=dtype) if out is None else out
1196
+ vr = np.atleast_2d(toReturn)
1197
+ vr[:, 0] = v0[:, 1] * v1[2] - v0[:, 2] * v1[1]
1198
+ vr[:, 1] = v0[:, 2] * v1[0] - v0[:, 0] * v1[2]
1199
+ vr[:, 2] = v0[:, 0] * v1[1] - v0[:, 1] * v1[0]
1200
+
1201
+ if vr.shape[1] == 4:
1202
+ vr[:, 3] = dtype(1.0)
1203
+
1204
+ elif v0.ndim == 1 and v1.ndim == 2: # 1D x 2D
1205
+ toReturn = np.zeros(v1.shape, dtype=dtype) if out is None else out
1206
+ vr = np.atleast_2d(toReturn)
1207
+ vr[:, 0] = v1[:, 2] * v0[1] - v1[:, 1] * v0[2]
1208
+ vr[:, 1] = v1[:, 0] * v0[2] - v1[:, 2] * v0[0]
1209
+ vr[:, 2] = v1[:, 1] * v0[0] - v1[:, 0] * v0[1]
1210
+
1211
+ if vr.shape[1] == 4:
1212
+ vr[:, 3] = dtype(1.0)
1213
+
1214
+ else:
1215
+ raise ValueError("Input arguments have incorrect dimensions.")
1216
+
1217
+ return toReturn
1218
+
1219
+
1220
+ def project(v0, v1, out=None, dtype=None):
1221
+ """Project a vector onto another.
1222
+
1223
+ Parameters
1224
+ ----------
1225
+ v0 : array_like
1226
+ Vector can be Nx2, Nx3, or Nx4. If a 2D array is specified, rows are
1227
+ treated as separate vectors.
1228
+ v1 : array_like
1229
+ Vector to project onto `v0`.
1230
+ out : ndarray, optional
1231
+ Optional output array. Must be same `shape` and `dtype` as the expected
1232
+ output if `out` was not specified.
1233
+ dtype : dtype or str, optional
1234
+ Data type for computations can either be 'float32' or 'float64'. If
1235
+ `out` is specified, the data type of `out` is used and this argument is
1236
+ ignored. If `out` is not provided, 'float64' is used by default.
1237
+
1238
+ Returns
1239
+ -------
1240
+ ndarray or float
1241
+ Projection of vector `v0` on `v1`.
1242
+
1243
+ """
1244
+ if out is None:
1245
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1246
+ else:
1247
+ dtype = np.dtype(out.dtype).type
1248
+
1249
+ v0 = np.asarray(v0, dtype=dtype)
1250
+ v1 = np.asarray(v1, dtype=dtype)
1251
+
1252
+ if v0.ndim == v1.ndim == 2 or v0.ndim == 1 and v1.ndim == 2:
1253
+ toReturn = np.zeros_like(v1, dtype=dtype) if out is None else out
1254
+ toReturn[:, :] = v1[:, :]
1255
+ toReturn *= (dot(v0, v1, dtype=dtype) / length(v1))[:, np.newaxis]
1256
+ elif v0.ndim == v1.ndim == 1:
1257
+ toReturn = v1 * (dot(v0, v1, dtype=dtype) / np.sum(np.square(v1)))
1258
+ elif v0.ndim == 2 and v1.ndim == 1:
1259
+ toReturn = np.zeros_like(v0, dtype=dtype) if out is None else out
1260
+ toReturn[:, :] = v1[:]
1261
+ toReturn *= (dot(v0, v1, dtype=dtype) / length(v1))[:, np.newaxis]
1262
+ else:
1263
+ raise ValueError("Input arguments have invalid dimensions.")
1264
+
1265
+ toReturn += 0.0 # remove negative zeros
1266
+ return toReturn
1267
+
1268
+
1269
+ def lerp(v0, v1, t, out=None, dtype=None):
1270
+ """Linear interpolation (LERP) between two vectors/coordinates.
1271
+
1272
+ Parameters
1273
+ ----------
1274
+ v0 : array_like
1275
+ Initial vector/coordinate. Can be 2D where each row is a point.
1276
+ v1 : array_like
1277
+ Final vector/coordinate. Must be the same shape as `v0`.
1278
+ t : float
1279
+ Interpolation weight factor [0, 1].
1280
+ out : ndarray, optional
1281
+ Optional output array. Must be same `shape` and `dtype` as the expected
1282
+ output if `out` was not specified.
1283
+ dtype : dtype or str, optional
1284
+ Data type for computations can either be 'float32' or 'float64'. If
1285
+ `out` is specified, the data type of `out` is used and this argument is
1286
+ ignored. If `out` is not provided, 'float64' is used by default.
1287
+
1288
+ Returns
1289
+ -------
1290
+ ndarray
1291
+ Vector at `t` with same shape as `v0` and `v1`.
1292
+
1293
+ Examples
1294
+ --------
1295
+ Find the coordinate of the midpoint between two vectors::
1296
+
1297
+ u = [0., 0., 0.]
1298
+ v = [0., 0., 1.]
1299
+ midpoint = lerp(u, v, 0.5) # 0.5 to interpolate half-way between points
1300
+
1301
+ """
1302
+ if out is None:
1303
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1304
+ else:
1305
+ dtype = np.dtype(out.dtype).type
1306
+
1307
+ t = dtype(t)
1308
+ t0 = dtype(1.0) - t
1309
+ v0 = np.asarray(v0, dtype=dtype)
1310
+ v1 = np.asarray(v1, dtype=dtype)
1311
+
1312
+ toReturn = np.zeros_like(v0, dtype=dtype) if out is None else out
1313
+
1314
+ v0, v1, vr = np.atleast_2d(v0, v1, toReturn)
1315
+ vr[:, :] = v0 * t0
1316
+ vr[:, :] += v1 * t
1317
+
1318
+ return toReturn
1319
+
1320
+
1321
+ def distance(v0, v1, out=None, dtype=None):
1322
+ """Get the distance between vectors/coordinates.
1323
+
1324
+ The behaviour of this function depends on the format of the input arguments:
1325
+
1326
+ * If `v0` and `v1` are 1D, the distance is returned as a scalar and `out` is
1327
+ ignored.
1328
+ * If `v0` and `v1` are 2D, an array of distances between corresponding row
1329
+ vectors are returned.
1330
+ * If either `v0` and `v1` are 1D and 2D, an array of distances
1331
+ between each row of the 2D vector and the 1D vector are returned.
1332
+
1333
+ Parameters
1334
+ ----------
1335
+ v0, v1 : array_like
1336
+ Vectors to compute the distance between.
1337
+ out : ndarray, optional
1338
+ Optional output array. Must be same `shape` and `dtype` as the expected
1339
+ output if `out` was not specified.
1340
+ dtype : dtype or str, optional
1341
+ Data type for computations can either be 'float32' or 'float64'. If
1342
+ `out` is specified, the data type of `out` is used and this argument is
1343
+ ignored. If `out` is not provided, 'float64' is used by default.
1344
+
1345
+ Returns
1346
+ -------
1347
+ ndarray
1348
+ Distance between vectors `v0` and `v1`.
1349
+
1350
+ """
1351
+ if out is None:
1352
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1353
+ else:
1354
+ dtype = np.dtype(out.dtype).type
1355
+
1356
+ v0 = np.asarray(v0, dtype=dtype)
1357
+ v1 = np.asarray(v1, dtype=dtype)
1358
+
1359
+ if v0.ndim == v1.ndim == 2 or (v0.ndim == 2 and v1.ndim == 1):
1360
+ dist = np.zeros((v1.shape[0],), dtype=dtype) if out is None else out
1361
+ dist[:] = np.sqrt(np.sum(np.square(v1 - v0), axis=1))
1362
+ elif v0.ndim == v1.ndim == 1:
1363
+ dist = np.sqrt(np.sum(np.square(v1 - v0)))
1364
+ elif v0.ndim == 1 and v1.ndim == 2:
1365
+ dist = np.zeros((v1.shape[0],), dtype=dtype) if out is None else out
1366
+ dist[:] = np.sqrt(np.sum(np.square(v0 - v1), axis=1))
1367
+ else:
1368
+ raise ValueError("Input arguments have invalid dimensions.")
1369
+
1370
+ return dist
1371
+
1372
+
1373
+ def perp(v, n, norm=True, out=None, dtype=None):
1374
+ """Project `v` to be a perpendicular axis of `n`.
1375
+
1376
+ Parameters
1377
+ ----------
1378
+ v : array_like
1379
+ Vector to project [x, y, z], may be Nx3.
1380
+ n : array_like
1381
+ Normal vector [x, y, z], may be Nx3.
1382
+ norm : bool
1383
+ Normalize the resulting axis. Default is `True`.
1384
+ out : ndarray, optional
1385
+ Optional output array. Must be same `shape` and `dtype` as the expected
1386
+ output if `out` was not specified.
1387
+ dtype : dtype or str, optional
1388
+ Data type for computations can either be 'float32' or 'float64'. If
1389
+ `out` is specified, the data type of `out` is used and this argument is
1390
+ ignored. If `out` is not provided, 'float64' is used by default.
1391
+
1392
+ Returns
1393
+ -------
1394
+ ndarray
1395
+ Perpendicular axis of `n` from `v`.
1396
+
1397
+ Examples
1398
+ --------
1399
+ Determine the local `up` (y-axis) of a surface or plane given `normal`::
1400
+
1401
+ normal = [0., 0.70710678, 0.70710678]
1402
+ up = [1., 0., 0.]
1403
+
1404
+ yaxis = perp(up, normal)
1405
+
1406
+ Do a cross product to get the x-axis perpendicular to both::
1407
+
1408
+ xaxis = cross(yaxis, normal)
1409
+
1410
+ """
1411
+ if out is None:
1412
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1413
+ else:
1414
+ dtype = np.dtype(out.dtype).type
1415
+
1416
+ v = np.asarray(v, dtype=dtype)
1417
+ n = np.asarray(n, dtype=dtype)
1418
+
1419
+ toReturn = np.zeros_like(v, dtype=dtype) if out is None else out
1420
+ v2d, n2d, r2d = np.atleast_2d(v, n, toReturn)
1421
+
1422
+ # from GLM `glm/gtx/perpendicular.inl`
1423
+ r2d[:, :] = v2d - project(v2d, n2d, dtype=dtype)
1424
+
1425
+ if norm:
1426
+ normalize(toReturn, out=toReturn)
1427
+
1428
+ toReturn += 0.0 # clear negative zeros
1429
+
1430
+ return toReturn
1431
+
1432
+
1433
+ def bisector(v0, v1, norm=False, out=None, dtype=None):
1434
+ """Get the angle bisector.
1435
+
1436
+ Computes a vector which bisects the angle between `v0` and `v1`. Input
1437
+ vectors `v0` and `v1` must be non-zero.
1438
+
1439
+ Parameters
1440
+ ----------
1441
+ v0, v1 : array_like
1442
+ Vectors to bisect [x, y, z]. Must be non-zero in length and have the
1443
+ same shape. Inputs can be Nx3 where the bisector for corresponding
1444
+ rows will be returned.
1445
+ norm : bool, optional
1446
+ Normalize the resulting bisector. Default is `False`.
1447
+ out : ndarray, optional
1448
+ Optional output array. Must be same `shape` and `dtype` as the expected
1449
+ output if `out` was not specified.
1450
+ dtype : dtype or str, optional
1451
+ Data type for computations can either be 'float32' or 'float64'. If
1452
+ `out` is specified, the data type of `out` is used and this argument is
1453
+ ignored. If `out` is not provided, 'float64' is used by default.
1454
+
1455
+ Returns
1456
+ -------
1457
+ ndarray
1458
+ Bisecting vector [x, y, z].
1459
+
1460
+ """
1461
+ if out is None:
1462
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1463
+ else:
1464
+ dtype = np.dtype(out.dtype).type
1465
+
1466
+ v0 = np.asarray(v0, dtype=dtype)
1467
+ v1 = np.asarray(v1, dtype=dtype)
1468
+
1469
+ assert v0.shape == v1.shape
1470
+
1471
+ toReturn = np.zeros_like(v0, dtype=dtype) if out is None else out
1472
+
1473
+ v02d, v12d, r2d = np.atleast_2d(v0, v1, toReturn)
1474
+
1475
+ r2d[:, :] = v02d * length(v12d, dtype=dtype)[:, np.newaxis] + \
1476
+ v12d * length(v02d, dtype=dtype)[:, np.newaxis]
1477
+
1478
+ if norm:
1479
+ normalize(r2d, out=r2d)
1480
+
1481
+ return toReturn
1482
+
1483
+
1484
+ def angleTo(v, point, degrees=True, out=None, dtype=None):
1485
+ """Get the relative angle to a point from a vector.
1486
+
1487
+ The behaviour of this function depends on the format of the input arguments:
1488
+
1489
+ * If `v0` and `v1` are 1D, the angle is returned as a scalar and `out` is
1490
+ ignored.
1491
+ * If `v0` and `v1` are 2D, an array of angles between corresponding row
1492
+ vectors are returned.
1493
+ * If either `v0` and `v1` are 1D and 2D, an array of angles
1494
+ between each row of the 2D vector and the 1D vector are returned.
1495
+
1496
+ Parameters
1497
+ ----------
1498
+ v : array_like
1499
+ Direction vector [x, y, z].
1500
+ point : array_like
1501
+ Point(s) to compute angle to from vector `v`.
1502
+ degrees : bool, optional
1503
+ Return the resulting angles in degrees. If `False`, angles will be
1504
+ returned in radians. Default is `True`.
1505
+ out : ndarray, optional
1506
+ Optional output array. Must be same `shape` and `dtype` as the expected
1507
+ output if `out` was not specified.
1508
+ dtype : dtype or str, optional
1509
+ Data type for computations can either be 'float32' or 'float64'. If
1510
+ `out` is specified, the data type of `out` is used and this argument is
1511
+ ignored. If `out` is not provided, 'float64' is used by default.
1512
+
1513
+ Returns
1514
+ -------
1515
+ ndarray
1516
+ Distance between vectors `v0` and `v1`.
1517
+
1518
+ """
1519
+ if out is None:
1520
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1521
+ else:
1522
+ dtype = np.dtype(out.dtype).type
1523
+
1524
+ v = np.asarray(v, dtype=dtype)
1525
+ point = np.asarray(point, dtype=dtype)
1526
+
1527
+ if v.ndim == point.ndim == 2 or (v.ndim == 2 and point.ndim == 1):
1528
+ angle = np.zeros((v.shape[0],), dtype=dtype) if out is None else out
1529
+ u = np.sqrt(length(v, squared=True, dtype=dtype) *
1530
+ length(point, squared=True, dtype=dtype))
1531
+ angle[:] = np.arccos(dot(v, point, dtype=dtype) / u)
1532
+ elif v.ndim == 1 and point.ndim == 2:
1533
+ angle = np.zeros((point.shape[0],), dtype=dtype) if out is None else out
1534
+ u = np.sqrt(length(v, squared=True, dtype=dtype) *
1535
+ length(point, squared=True, dtype=dtype))
1536
+ angle[:] = np.arccos(dot(v, point, dtype=dtype) / u)
1537
+ elif v.ndim == point.ndim == 1:
1538
+ u = np.sqrt(length(v, squared=True, dtype=dtype) *
1539
+ length(point, squared=True, dtype=dtype))
1540
+ angle = np.arccos(dot(v, point, dtype=dtype) / u)
1541
+ else:
1542
+ raise ValueError("Input arguments have invalid dimensions.")
1543
+
1544
+ return np.degrees(angle) if degrees else angle
1545
+
1546
+
1547
+ def sortClockwise(verts):
1548
+ """Sort vertices clockwise from 12 O'Clock (aka vertex (0, 1)).
1549
+
1550
+ Parameters
1551
+ ----------
1552
+ verts : array
1553
+ Array of vertices to sort.
1554
+
1555
+ Returns
1556
+ -------
1557
+ array
1558
+ Vertices sorted clockwise from 12 O'Clock.
1559
+
1560
+ """
1561
+ # Blank array of angles
1562
+ angles = []
1563
+ # Calculate angle of each vertex
1564
+ for vert in verts:
1565
+ # Get angle
1566
+ ang = angleTo(v=[0, 1], point=vert)
1567
+ # Flip angle if we're past 6 O'clock
1568
+ if vert[0] < 0:
1569
+ ang = 360 - ang
1570
+ # Append to angles array
1571
+ angles.append(ang)
1572
+ # Sort vertices by angles array values
1573
+ verts = [x for _, x in sorted(zip(angles, verts), key=lambda pair: pair[0])]
1574
+
1575
+ return verts
1576
+
1577
+
1578
+ def surfaceNormal(tri, norm=True, out=None, dtype=None):
1579
+ """Compute the surface normal of a given triangle.
1580
+
1581
+ Parameters
1582
+ ----------
1583
+ tri : array_like
1584
+ Triangle vertices as 2D (3x3) array [p0, p1, p2] where each vertex is a
1585
+ length 3 array [vx, xy, vz]. The input array can be 3D (Nx3x3) to
1586
+ specify multiple triangles.
1587
+ norm : bool, optional
1588
+ Normalize computed surface normals if ``True``, default is ``True``.
1589
+ out : ndarray, optional
1590
+ Optional output array. Must have one fewer dimensions than `tri`. The
1591
+ shape of the last dimension must be 3.
1592
+ dtype : dtype or str, optional
1593
+ Data type for computations can either be 'float32' or 'float64'. If
1594
+ `out` is specified, the data type of `out` is used and this argument is
1595
+ ignored. If `out` is not provided, 'float64' is used by default.
1596
+
1597
+ Returns
1598
+ -------
1599
+ ndarray
1600
+ Surface normal of triangle `tri`.
1601
+
1602
+ Examples
1603
+ --------
1604
+ Compute the surface normal of a triangle::
1605
+
1606
+ vertices = [[-1., 0., 0.], [0., 1., 0.], [1, 0, 0]]
1607
+ norm = surfaceNormal(vertices)
1608
+
1609
+ Find the normals for multiple triangles, and put results in a pre-allocated
1610
+ array::
1611
+
1612
+ vertices = [[[-1., 0., 0.], [0., 1., 0.], [1, 0, 0]], # 2x3x3
1613
+ [[1., 0., 0.], [0., 1., 0.], [-1, 0, 0]]]
1614
+ normals = np.zeros((2, 3)) # normals for two triangles
1615
+ surfaceNormal(vertices, out=normals)
1616
+
1617
+ """
1618
+ if out is None:
1619
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1620
+ else:
1621
+ dtype = np.dtype(out.dtype).type
1622
+
1623
+ tris = np.asarray(tri, dtype=dtype)
1624
+ if tris.ndim == 2:
1625
+ tris = np.expand_dims(tri, axis=0)
1626
+
1627
+ if tris.shape[0] == 1:
1628
+ toReturn = np.zeros((3,), dtype=dtype) if out is None else out
1629
+ else:
1630
+ if out is None:
1631
+ toReturn = np.zeros((tris.shape[0], 3), dtype=dtype)
1632
+ else:
1633
+ toReturn = out
1634
+
1635
+ # from https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
1636
+ nr = np.atleast_2d(toReturn)
1637
+ u = tris[:, 1, :] - tris[:, 0, :]
1638
+ v = tris[:, 2, :] - tris[:, 1, :]
1639
+ nr[:, 0] = u[:, 1] * v[:, 2] - u[:, 2] * v[:, 1]
1640
+ nr[:, 1] = u[:, 2] * v[:, 0] - u[:, 0] * v[:, 2]
1641
+ nr[:, 2] = u[:, 0] * v[:, 1] - u[:, 1] * v[:, 0]
1642
+
1643
+ if norm:
1644
+ normalize(nr, out=nr)
1645
+
1646
+ return toReturn
1647
+
1648
+
1649
+ def surfaceBitangent(tri, uv, norm=True, out=None, dtype=None):
1650
+ """Compute the bitangent vector of a given triangle.
1651
+
1652
+ This function can be used to generate bitangent vertex attributes for normal
1653
+ mapping. After computing bitangents, one may orthogonalize them with vertex
1654
+ normals using the :func:`orthogonalize` function, or within the fragment
1655
+ shader. Uses texture coordinates at each triangle vertex to determine the
1656
+ direction of the vector.
1657
+
1658
+ Parameters
1659
+ ----------
1660
+ tri : array_like
1661
+ Triangle vertices as 2D (3x3) array [p0, p1, p2] where each vertex is a
1662
+ length 3 array [vx, xy, vz]. The input array can be 3D (Nx3x3) to
1663
+ specify multiple triangles.
1664
+ uv : array_like
1665
+ Texture coordinates associated with each face vertex as a 2D array (3x2)
1666
+ where each texture coordinate is length 2 array [u, v]. The input array
1667
+ can be 3D (Nx3x2) to specify multiple texture coordinates if multiple
1668
+ triangles are specified.
1669
+ norm : bool, optional
1670
+ Normalize computed bitangents if ``True``, default is ``True``.
1671
+ out : ndarray, optional
1672
+ Optional output array. Must have one fewer dimensions than `tri`. The
1673
+ shape of the last dimension must be 3.
1674
+ dtype : dtype or str, optional
1675
+ Data type for computations can either be 'float32' or 'float64'. If
1676
+ `out` is specified, the data type of `out` is used and this argument is
1677
+ ignored. If `out` is not provided, 'float64' is used by default.
1678
+
1679
+ Returns
1680
+ -------
1681
+ ndarray
1682
+ Surface bitangent of triangle `tri`.
1683
+
1684
+ Examples
1685
+ --------
1686
+ Computing the bitangents for two triangles from vertex and texture
1687
+ coordinates (UVs)::
1688
+
1689
+ # array of triangle vertices (2x3x3)
1690
+ tri = np.asarray([
1691
+ [(-1.0, 1.0, 0.0), (-1.0, -1.0, 0.0), (1.0, -1.0, 0.0)], # 1
1692
+ [(-1.0, 1.0, 0.0), (-1.0, -1.0, 0.0), (1.0, -1.0, 0.0)]]) # 2
1693
+
1694
+ # array of triangle texture coordinates (2x3x2)
1695
+ uv = np.asarray([
1696
+ [(0.0, 1.0), (0.0, 0.0), (1.0, 0.0)], # 1
1697
+ [(0.0, 1.0), (0.0, 0.0), (1.0, 0.0)]]) # 2
1698
+
1699
+ bitangents = surfaceBitangent(tri, uv, norm=True) # bitangets (2x3)
1700
+
1701
+ """
1702
+ if out is None:
1703
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1704
+ else:
1705
+ dtype = np.dtype(out.dtype).type
1706
+
1707
+ tris = np.asarray(tri, dtype=dtype)
1708
+ if tris.ndim == 2:
1709
+ tris = np.expand_dims(tri, axis=0)
1710
+
1711
+ if tris.shape[0] == 1:
1712
+ toReturn = np.zeros((3,), dtype=dtype) if out is None else out
1713
+ else:
1714
+ if out is None:
1715
+ toReturn = np.zeros((tris.shape[0], 3), dtype=dtype)
1716
+ else:
1717
+ toReturn = out
1718
+
1719
+ uvs = np.asarray(uv, dtype=dtype)
1720
+ if uvs.ndim == 2:
1721
+ uvs = np.expand_dims(uvs, axis=0)
1722
+
1723
+ # based off the implementation from
1724
+ # https://learnopengl.com/Advanced-Lighting/Normal-Mapping
1725
+ e1 = tris[:, 1, :] - tris[:, 0, :]
1726
+ e2 = tris[:, 2, :] - tris[:, 0, :]
1727
+ d1 = uvs[:, 1, :] - uvs[:, 0, :]
1728
+ d2 = uvs[:, 2, :] - uvs[:, 0, :]
1729
+
1730
+ # compute the bitangent
1731
+ nr = np.atleast_2d(toReturn)
1732
+ nr[:, 0] = -d2[:, 0] * e1[:, 0] + d1[:, 0] * e2[:, 0]
1733
+ nr[:, 1] = -d2[:, 0] * e1[:, 1] + d1[:, 0] * e2[:, 1]
1734
+ nr[:, 2] = -d2[:, 0] * e1[:, 2] + d1[:, 0] * e2[:, 2]
1735
+
1736
+ f = dtype(1.0) / (d1[:, 0] * d2[:, 1] - d2[:, 0] * d1[:, 1])
1737
+ nr *= f[:, np.newaxis]
1738
+
1739
+ if norm:
1740
+ normalize(toReturn, out=toReturn, dtype=dtype)
1741
+
1742
+ return toReturn
1743
+
1744
+
1745
+ def surfaceTangent(tri, uv, norm=True, out=None, dtype=None):
1746
+ """Compute the tangent vector of a given triangle.
1747
+
1748
+ This function can be used to generate tangent vertex attributes for normal
1749
+ mapping. After computing tangents, one may orthogonalize them with vertex
1750
+ normals using the :func:`orthogonalize` function, or within the fragment
1751
+ shader. Uses texture coordinates at each triangle vertex to determine the
1752
+ direction of the vector.
1753
+
1754
+ Parameters
1755
+ ----------
1756
+ tri : array_like
1757
+ Triangle vertices as 2D (3x3) array [p0, p1, p2] where each vertex is a
1758
+ length 3 array [vx, xy, vz]. The input array can be 3D (Nx3x3) to
1759
+ specify multiple triangles.
1760
+ uv : array_like
1761
+ Texture coordinates associated with each face vertex as a 2D array (3x2)
1762
+ where each texture coordinate is length 2 array [u, v]. The input array
1763
+ can be 3D (Nx3x2) to specify multiple texture coordinates if multiple
1764
+ triangles are specified. If so `N` must be the same size as the first
1765
+ dimension of `tri`.
1766
+ norm : bool, optional
1767
+ Normalize computed tangents if ``True``, default is ``True``.
1768
+ out : ndarray, optional
1769
+ Optional output array. Must have one fewer dimensions than `tri`. The
1770
+ shape of the last dimension must be 3.
1771
+ dtype : dtype or str, optional
1772
+ Data type for computations can either be 'float32' or 'float64'. If
1773
+ `out` is specified, the data type of `out` is used and this argument is
1774
+ ignored. If `out` is not provided, 'float64' is used by default.
1775
+
1776
+ Returns
1777
+ -------
1778
+ ndarray
1779
+ Surface normal of triangle `tri`.
1780
+
1781
+ Examples
1782
+ --------
1783
+ Compute surface normals, tangents, and bitangents for a list of triangles::
1784
+
1785
+ # triangle vertices (2x3x3)
1786
+ vertices = [[[-1., 0., 0.], [0., 1., 0.], [1, 0, 0]],
1787
+ [[1., 0., 0.], [0., 1., 0.], [-1, 0, 0]]]
1788
+
1789
+ # array of triangle texture coordinates (2x3x2)
1790
+ uv = np.asarray([
1791
+ [(0.0, 1.0), (0.0, 0.0), (1.0, 0.0)], # 1
1792
+ [(0.0, 1.0), (0.0, 0.0), (1.0, 0.0)]]) # 2
1793
+
1794
+ normals = surfaceNormal(vertices)
1795
+ tangents = surfaceTangent(vertices, uv)
1796
+ bitangents = cross(normals, tangents) # or use `surfaceBitangent`
1797
+
1798
+ Orthogonalize a surface tangent with a vertex normal vector to get the
1799
+ vertex tangent and bitangent vectors::
1800
+
1801
+ vertexTangent = orthogonalize(faceTangent, vertexNormal)
1802
+ vertexBitangent = cross(vertexTangent, vertexNormal)
1803
+
1804
+ Ensure computed vectors have the same handedness, if not, flip the tangent
1805
+ vector (important for applications like normal mapping)::
1806
+
1807
+ # tangent, bitangent, and normal are 2D
1808
+ tangent[dot(cross(normal, tangent), bitangent) < 0.0, :] *= -1.0
1809
+
1810
+ """
1811
+ if out is None:
1812
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1813
+ else:
1814
+ dtype = np.dtype(out.dtype).type
1815
+
1816
+ tris = np.asarray(tri, dtype=dtype)
1817
+ if tris.ndim == 2:
1818
+ tris = np.expand_dims(tri, axis=0)
1819
+
1820
+ if tris.shape[0] == 1:
1821
+ toReturn = np.zeros((3,), dtype=dtype) if out is None else out
1822
+ else:
1823
+ if out is None:
1824
+ toReturn = np.zeros((tris.shape[0], 3), dtype=dtype)
1825
+ else:
1826
+ toReturn = out
1827
+
1828
+ uvs = np.asarray(uv, dtype=dtype)
1829
+ if uvs.ndim == 2:
1830
+ uvs = np.expand_dims(uvs, axis=0)
1831
+
1832
+ # based off the implementation from
1833
+ # https://learnopengl.com/Advanced-Lighting/Normal-Mapping
1834
+ e1 = tris[:, 1, :] - tris[:, 0, :]
1835
+ e2 = tris[:, 2, :] - tris[:, 0, :]
1836
+ d1 = uvs[:, 1, :] - uvs[:, 0, :]
1837
+ d2 = uvs[:, 2, :] - uvs[:, 0, :]
1838
+
1839
+ # compute the bitangent
1840
+ nr = np.atleast_2d(toReturn)
1841
+ nr[:, 0] = d2[:, 1] * e1[:, 0] - d1[:, 1] * e2[:, 0]
1842
+ nr[:, 1] = d2[:, 1] * e1[:, 1] - d1[:, 1] * e2[:, 1]
1843
+ nr[:, 2] = d2[:, 1] * e1[:, 2] - d1[:, 1] * e2[:, 2]
1844
+
1845
+ f = dtype(1.0) / (d1[:, 0] * d2[:, 1] - d2[:, 0] * d1[:, 1])
1846
+ nr *= f[:, np.newaxis]
1847
+
1848
+ if norm:
1849
+ normalize(toReturn, out=toReturn, dtype=dtype)
1850
+
1851
+ return toReturn
1852
+
1853
+
1854
+ def vertexNormal(faceNorms, norm=True, out=None, dtype=None):
1855
+ """Compute a vertex normal from shared triangles.
1856
+
1857
+ This function computes a vertex normal by averaging the surface normals of
1858
+ the triangles it belongs to. If model has no vertex normals, first use
1859
+ :func:`surfaceNormal` to compute them, then run :func:`vertexNormal` to
1860
+ compute vertex normal attributes.
1861
+
1862
+ While this function is mainly used to compute vertex normals, it can also
1863
+ be supplied triangle tangents and bitangents.
1864
+
1865
+ Parameters
1866
+ ----------
1867
+ faceNorms : array_like
1868
+ An array (Nx3) of surface normals.
1869
+ norm : bool, optional
1870
+ Normalize computed normals if ``True``, default is ``True``.
1871
+ out : ndarray, optional
1872
+ Optional output array.
1873
+ dtype : dtype or str, optional
1874
+ Data type for computations can either be 'float32' or 'float64'. If
1875
+ `out` is specified, the data type of `out` is used and this argument is
1876
+ ignored. If `out` is not provided, 'float64' is used by default.
1877
+
1878
+ Returns
1879
+ -------
1880
+ ndarray
1881
+ Vertex normal.
1882
+
1883
+ Examples
1884
+ --------
1885
+ Compute a vertex normal from the face normals of the triangles it belongs
1886
+ to::
1887
+
1888
+ normals = [[1., 0., 0.], [0., 1., 0.]] # adjacent face normals
1889
+ vertexNorm = vertexNormal(normals)
1890
+
1891
+ """
1892
+ if out is None:
1893
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1894
+ else:
1895
+ dtype = np.dtype(out.dtype).type
1896
+
1897
+ triNorms2d = np.atleast_2d(np.asarray(faceNorms, dtype=dtype))
1898
+ nFaces = triNorms2d.shape[0]
1899
+
1900
+ if out is None:
1901
+ toReturn = np.zeros((3,), dtype=dtype)
1902
+ else:
1903
+ toReturn = out
1904
+
1905
+ toReturn[0] = np.sum(triNorms2d[:, 0])
1906
+ toReturn[1] = np.sum(triNorms2d[:, 1])
1907
+ toReturn[2] = np.sum(triNorms2d[:, 2])
1908
+ toReturn /= nFaces
1909
+
1910
+ if norm:
1911
+ normalize(toReturn, out=toReturn, dtype=dtype)
1912
+
1913
+ return toReturn
1914
+
1915
+
1916
+ def fixTangentHandedness(tangents, normals, bitangents, out=None, dtype=None):
1917
+ """Ensure the handedness of tangent vectors are all the same.
1918
+
1919
+ Often 3D computed tangents may not have the same handedness due to how
1920
+ texture coordinates are specified. This function takes input surface vectors
1921
+ are ensures that tangents have the same handedness. Use this function if you
1922
+ notice that normal mapping shading appears reversed with respect to the
1923
+ incident light direction. The output array of corrected tangents can be used
1924
+ inplace of the original.
1925
+
1926
+ Parameters
1927
+ ----------
1928
+ tangents, normals, bitangents : array_like
1929
+ Input Nx3 arrays of triangle tangents, normals and bitangents. All
1930
+ arrays must have the same size.
1931
+ out : ndarray, optional
1932
+ Optional output array for tangents. If not specified, a new array of
1933
+ tangents will be allocated.
1934
+ dtype : dtype or str, optional
1935
+ Data type for computations can either be 'float32' or 'float64'. If
1936
+ `out` is specified, the data type of `out` is used and this argument is
1937
+ ignored. If `out` is not provided, 'float64' is used by default.
1938
+
1939
+ Returns
1940
+ -------
1941
+ ndarray
1942
+ Array of tangents with handedness corrected.
1943
+
1944
+ """
1945
+ if out is None:
1946
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1947
+ else:
1948
+ dtype = np.dtype(out.dtype).type
1949
+
1950
+ tangents = np.asarray(tangents, dtype=dtype)
1951
+ normals = np.asarray(normals, dtype=dtype)
1952
+ bitangents = np.asarray(bitangents, dtype=dtype)
1953
+
1954
+ toReturn = np.zeros_like(tangents, dtype=dtype) if out is None else out
1955
+ toReturn[:, :] = tangents
1956
+ toReturn[dot(cross(normals, tangents, dtype=dtype),
1957
+ bitangents, dtype=dtype) < 0.0, :] *= -1.0
1958
+
1959
+ return toReturn
1960
+
1961
+
1962
+ # ------------------------------------------------------------------------------
1963
+ # Collision Detection, Interaction and Kinematics
1964
+ #
1965
+ def fitBBox(points, dtype=None):
1966
+ """Fit an axis-aligned bounding box around points.
1967
+
1968
+ This computes the minimum and maximum extents for a bounding box to
1969
+ completely enclose `points`. Keep in mind the output in bounds are
1970
+ axis-aligned and may not optimally fits the points (i.e. fits the points
1971
+ with the minimum required volume). However, this should work well enough for
1972
+ applications such as visibility testing (see
1973
+ `~psychopy.tools.viewtools.volumeVisible` for more information..
1974
+
1975
+ Parameters
1976
+ ----------
1977
+ points : array_like
1978
+ Nx3 or Nx4 array of points to fit the bounding box to.
1979
+ dtype : dtype or str, optional
1980
+ Data type for computations can either be 'float32' or 'float64'. If
1981
+ `out` is specified, the data type of `out` is used and this argument is
1982
+ ignored. If `out` is not provided, 'float64' is used by default.
1983
+
1984
+ Returns
1985
+ -------
1986
+ ndarray
1987
+ Extents (mins, maxs) as a 2x3 array.
1988
+
1989
+ See Also
1990
+ --------
1991
+ computeBBoxCorners : Convert bounding box extents to corners.
1992
+
1993
+ """
1994
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1995
+
1996
+ points = np.asarray(points, dtype=dtype)
1997
+ extents = np.zeros((2, 3), dtype=dtype)
1998
+
1999
+ extents[0, :] = (np.min(points[:, 0]),
2000
+ np.min(points[:, 1]),
2001
+ np.min(points[:, 2]))
2002
+ extents[1, :] = (np.max(points[:, 0]),
2003
+ np.max(points[:, 1]),
2004
+ np.max(points[:, 2]))
2005
+
2006
+ return extents
2007
+
2008
+
2009
+ def computeBBoxCorners(extents, dtype=None):
2010
+ """Get the corners of an axis-aligned bounding box.
2011
+
2012
+ Parameters
2013
+ ----------
2014
+ extents : array_like
2015
+ 2x3 array indicating the minimum and maximum extents of the bounding
2016
+ box.
2017
+ dtype : dtype or str, optional
2018
+ Data type for computations can either be 'float32' or 'float64'. If
2019
+ `out` is specified, the data type of `out` is used and this argument is
2020
+ ignored. If `out` is not provided, 'float64' is used by default.
2021
+
2022
+ Returns
2023
+ -------
2024
+ ndarray
2025
+ 8x4 array of points defining the corners of the bounding box.
2026
+
2027
+ Examples
2028
+ --------
2029
+ Compute the corner points of a bounding box::
2030
+
2031
+ minExtent = [-1, -1, -1]
2032
+ maxExtent = [1, 1, 1]
2033
+ corners = computeBBoxCorners([minExtent, maxExtent])
2034
+
2035
+ # [[ 1. 1. 1. 1.]
2036
+ # [-1. 1. 1. 1.]
2037
+ # [ 1. -1. 1. 1.]
2038
+ # [-1. -1. 1. 1.]
2039
+ # [ 1. 1. -1. 1.]
2040
+ # [-1. 1. -1. 1.]
2041
+ # [ 1. -1. -1. 1.]
2042
+ # [-1. -1. -1. 1.]]
2043
+
2044
+ """
2045
+ extents = np.asarray(extents, dtype=dtype)
2046
+
2047
+ assert extents.shape == (2, 3,)
2048
+
2049
+ corners = np.zeros((8, 4), dtype=dtype)
2050
+ idx = np.arange(0, 8)
2051
+ corners[:, 0] = np.where(idx[:] & 1, extents[0, 0], extents[1, 0])
2052
+ corners[:, 1] = np.where(idx[:] & 2, extents[0, 1], extents[1, 1])
2053
+ corners[:, 2] = np.where(idx[:] & 4, extents[0, 2], extents[1, 2])
2054
+ corners[:, 3] = 1.0
2055
+
2056
+ return corners
2057
+
2058
+
2059
+ def intersectRayPlane(rayOrig, rayDir, planeOrig, planeNormal, dtype=None):
2060
+ """Get the point which a ray intersects a plane.
2061
+
2062
+ Parameters
2063
+ ----------
2064
+ rayOrig : array_like
2065
+ Origin of the line in space [x, y, z].
2066
+ rayDir : array_like
2067
+ Direction vector of the line [x, y, z].
2068
+ planeOrig : array_like
2069
+ Origin of the plane to test [x, y, z].
2070
+ planeNormal : array_like
2071
+ Normal vector of the plane [x, y, z].
2072
+ dtype : dtype or str, optional
2073
+ Data type for computations can either be 'float32' or 'float64'. If
2074
+ `out` is specified, the data type of `out` is used and this argument is
2075
+ ignored. If `out` is not provided, 'float64' is used by default.
2076
+
2077
+ Returns
2078
+ -------
2079
+ tuple or None
2080
+ Position (`ndarray`) in space which the line intersects the plane and
2081
+ the distance the intersect occurs from the origin (`float`). `None` is
2082
+ returned if the line does not intersect the plane at a single point or
2083
+ at all.
2084
+
2085
+ Examples
2086
+ --------
2087
+ Find the point in the scene a ray intersects the plane::
2088
+
2089
+ # plane information
2090
+ planeOrigin = [0, 0, 0]
2091
+ planeNormal = [0, 0, 1]
2092
+ planeUpAxis = perp([0, 1, 0], planeNormal)
2093
+
2094
+ # ray
2095
+ rayDir = [0, 0, -1]
2096
+ rayOrigin = [0, 0, 5]
2097
+
2098
+ # get the intersect and distance in 3D world space
2099
+ pnt, dist = intersectRayPlane(rayOrigin, rayDir, planeOrigin, planeNormal)
2100
+
2101
+ """
2102
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2103
+
2104
+ # based off the method from GLM
2105
+ rayOrig = np.asarray(rayOrig, dtype=dtype)
2106
+ rayDir = np.asarray(rayDir, dtype=dtype)
2107
+ planeOrig = np.asarray(planeOrig, dtype=dtype)
2108
+ planeNormal = np.asarray(planeNormal, dtype=dtype)
2109
+
2110
+ denom = dot(rayDir, planeNormal, dtype=dtype)
2111
+ if denom == 0.0:
2112
+ return None
2113
+
2114
+ # distance to collision
2115
+ dist = dot((planeOrig - rayOrig), planeNormal, dtype=dtype) / denom
2116
+ intersect = dist * rayDir + rayOrig
2117
+
2118
+ return intersect, dist
2119
+
2120
+
2121
+ def intersectRaySphere(rayOrig, rayDir, sphereOrig=(0., 0., 0.), sphereRadius=1.0,
2122
+ dtype=None):
2123
+ """Calculate the points which a ray/line intersects a sphere (if any).
2124
+
2125
+ Get the 3D coordinate of the point which the ray intersects the sphere and
2126
+ the distance to the point from `orig`. The nearest point is returned if
2127
+ the line intersects the sphere at multiple locations. All coordinates should
2128
+ be in world/scene units.
2129
+
2130
+ Parameters
2131
+ ----------
2132
+ rayOrig : array_like
2133
+ Origin of the ray in space [x, y, z].
2134
+ rayDir : array_like
2135
+ Direction vector of the ray [x, y, z], should be normalized.
2136
+ sphereOrig : array_like
2137
+ Origin of the sphere to test [x, y, z].
2138
+ sphereRadius : float
2139
+ Sphere radius to test in scene units.
2140
+ dtype : dtype or str, optional
2141
+ Data type for computations can either be 'float32' or 'float64'. If
2142
+ `out` is specified, the data type of `out` is used and this argument is
2143
+ ignored. If `out` is not provided, 'float64' is used by default.
2144
+
2145
+ Returns
2146
+ -------
2147
+ tuple
2148
+ Coordinate in world space of the intersection and distance in scene
2149
+ units from `orig`. Returns `None` if there is no intersection.
2150
+
2151
+ """
2152
+ # based off example from https://antongerdelan.net/opengl/raycasting.html
2153
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2154
+
2155
+ rayOrig = np.asarray(rayOrig, dtype=dtype)
2156
+ rayDir = np.asarray(rayDir, dtype=dtype)
2157
+ sphereOrig = np.asarray(sphereOrig, dtype=dtype)
2158
+ sphereRadius = np.asarray(sphereRadius, dtype=dtype)
2159
+
2160
+ d = rayOrig - sphereOrig
2161
+ b = np.dot(rayDir, d)
2162
+ c = np.dot(d, d) - np.square(sphereRadius)
2163
+ b2mc = np.square(b) - c # determinant
2164
+
2165
+ if b2mc < 0.0: # no roots, ray does not intersect sphere
2166
+ return None
2167
+
2168
+ u = np.sqrt(b2mc)
2169
+ nearestDist = np.minimum(-b + u, -b - u)
2170
+ pos = (rayDir * nearestDist) + rayOrig
2171
+
2172
+ return pos, nearestDist
2173
+
2174
+
2175
+ def intersectRayAABB(rayOrig, rayDir, boundsOffset, boundsExtents, dtype=None):
2176
+ """Find the point a ray intersects an axis-aligned bounding box (AABB).
2177
+
2178
+ Parameters
2179
+ ----------
2180
+ rayOrig : array_like
2181
+ Origin of the ray in space [x, y, z].
2182
+ rayDir : array_like
2183
+ Direction vector of the ray [x, y, z], should be normalized.
2184
+ boundsOffset : array_like
2185
+ Offset of the bounding box in the scene [x, y, z].
2186
+ boundsExtents : array_like
2187
+ Minimum and maximum extents of the bounding box.
2188
+ dtype : dtype or str, optional
2189
+ Data type for computations can either be 'float32' or 'float64'. If
2190
+ `out` is specified, the data type of `out` is used and this argument is
2191
+ ignored. If `out` is not provided, 'float64' is used by default.
2192
+
2193
+ Returns
2194
+ -------
2195
+ tuple
2196
+ Coordinate in world space of the intersection and distance in scene
2197
+ units from `rayOrig`. Returns `None` if there is no intersection.
2198
+
2199
+ Examples
2200
+ --------
2201
+ Get the point on an axis-aligned bounding box that the cursor is over and
2202
+ place a 3D stimulus there. The eye location is defined by `RigidBodyPose`
2203
+ object `camera`::
2204
+
2205
+ # get the mouse position on-screen
2206
+ mx, my = mouse.getPos()
2207
+
2208
+ # find the point which the ray intersects on the box
2209
+ result = intersectRayAABB(
2210
+ camera.pos,
2211
+ camera.transformNormal(win.coordToRay((mx, my))),
2212
+ myStim.pos,
2213
+ myStim.thePose.bounds.extents)
2214
+
2215
+ # if the ray intersects, set the position of the cursor object to it
2216
+ if result is not None:
2217
+ cursorModel.thePose.pos = result[0]
2218
+ cursorModel.draw() # don't draw anything if there is no intersect
2219
+
2220
+ Note that if the model is rotated, the bounding box may not be aligned
2221
+ anymore with the axes. Use `intersectRayOBB` if your model rotates.
2222
+
2223
+ """
2224
+ # based of the example provided here:
2225
+ # https://www.scratchapixel.com/lessons/3d-basic-rendering/
2226
+ # minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection
2227
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2228
+
2229
+ rayOrig = np.asarray(rayOrig, dtype=dtype)
2230
+ rayDir = np.asarray(rayDir, dtype=dtype)
2231
+ boundsOffset = np.asarray(boundsOffset, dtype=dtype)
2232
+ extents = np.asarray(boundsExtents, dtype=dtype) + boundsOffset
2233
+
2234
+ invDir = 1.0 / rayDir
2235
+ sign = np.zeros((3,), dtype=int)
2236
+ sign[invDir < 0.0] = 1
2237
+
2238
+ tmin = (extents[sign[0], 0] - rayOrig[0]) * invDir[0]
2239
+ tmax = (extents[1 - sign[0], 0] - rayOrig[0]) * invDir[0]
2240
+ tymin = (extents[sign[1], 1] - rayOrig[1]) * invDir[1]
2241
+ tymax = (extents[1 - sign[1], 1] - rayOrig[1]) * invDir[1]
2242
+
2243
+ if tmin > tymax or tymin > tmax:
2244
+ return None
2245
+
2246
+ if tymin > tmin:
2247
+ tmin = tymin
2248
+
2249
+ if tymax < tmax:
2250
+ tmax = tymax
2251
+
2252
+ tzmin = (extents[sign[2], 2] - rayOrig[2]) * invDir[2]
2253
+ tzmax = (extents[1 - sign[2], 2] - rayOrig[2]) * invDir[2]
2254
+
2255
+ if tmin > tzmax or tzmin > tmax:
2256
+ return None
2257
+
2258
+ if tzmin > tmin:
2259
+ tmin = tzmin
2260
+
2261
+ if tzmax < tmax:
2262
+ tmax = tzmax
2263
+
2264
+ if tmin < 0:
2265
+ if tmax < 0:
2266
+ return None
2267
+
2268
+ return (rayDir * tmin) + rayOrig, tmin
2269
+
2270
+
2271
+ def intersectRayOBB(rayOrig, rayDir, modelMatrix, boundsExtents, dtype=None):
2272
+ """Find the point a ray intersects an oriented bounding box (OBB).
2273
+
2274
+ Parameters
2275
+ ----------
2276
+ rayOrig : array_like
2277
+ Origin of the ray in space [x, y, z].
2278
+ rayDir : array_like
2279
+ Direction vector of the ray [x, y, z], should be normalized.
2280
+ modelMatrix : array_like
2281
+ 4x4 model matrix of the object and bounding box.
2282
+ boundsExtents : array_like
2283
+ Minimum and maximum extents of the bounding box.
2284
+ dtype : dtype or str, optional
2285
+ Data type for computations can either be 'float32' or 'float64'. If
2286
+ `out` is specified, the data type of `out` is used and this argument is
2287
+ ignored. If `out` is not provided, 'float64' is used by default.
2288
+
2289
+ Returns
2290
+ -------
2291
+ tuple
2292
+ Coordinate in world space of the intersection and distance in scene
2293
+ units from `rayOrig`. Returns `None` if there is no intersection.
2294
+
2295
+ Examples
2296
+ --------
2297
+ Get the point on an oriented bounding box that the cursor is over and place
2298
+ a 3D stimulus there. The eye location is defined by `RigidBodyPose` object
2299
+ `camera`::
2300
+
2301
+ # get the mouse position on-screen
2302
+ mx, my = mouse.getPos()
2303
+
2304
+ # find the point which the ray intersects on the box
2305
+ result = intersectRayOBB(
2306
+ camera.pos,
2307
+ camera.transformNormal(win.coordToRay((mx, my))),
2308
+ myStim.thePose.getModelMatrix(),
2309
+ myStim.thePose.bounds.extents)
2310
+
2311
+ # if the ray intersects, set the position of the cursor object to it
2312
+ if result is not None:
2313
+ cursorModel.thePose.pos = result[0]
2314
+ cursorModel.draw() # don't draw anything if there is no intersect
2315
+
2316
+ """
2317
+ # based off algorithm:
2318
+ # https://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/
2319
+ # picking-with-custom-ray-obb-function/
2320
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2321
+
2322
+ rayOrig = np.asarray(rayOrig, dtype=dtype)
2323
+ rayDir = np.asarray(rayDir, dtype=dtype)
2324
+ modelMatrix = np.asarray(modelMatrix, dtype=dtype)
2325
+ boundsOffset = np.asarray(modelMatrix[:3, 3], dtype=dtype)
2326
+ extents = np.asarray(boundsExtents, dtype=dtype)
2327
+
2328
+ tmin = 0.0
2329
+ tmax = np.finfo(dtype).max
2330
+ d = boundsOffset - rayOrig
2331
+
2332
+ # solve intersects for each pair of planes along each axis
2333
+ for i in range(3):
2334
+ axis = modelMatrix[:3, i]
2335
+ e = np.dot(axis, d)
2336
+ f = np.dot(rayDir, axis)
2337
+
2338
+ if np.fabs(f) > 1e-5:
2339
+ t1 = (e + extents[0, i]) / f
2340
+ t2 = (e + extents[1, i]) / f
2341
+
2342
+ if t1 > t2:
2343
+ temp = t1
2344
+ t1 = t2
2345
+ t2 = temp
2346
+
2347
+ if t2 < tmax:
2348
+ tmax = t2
2349
+
2350
+ if t1 > tmin:
2351
+ tmin = t1
2352
+
2353
+ if tmin > tmax:
2354
+ return None
2355
+
2356
+ else:
2357
+ # very close to parallel with the face
2358
+ if -e + extents[0, i] > 0.0 or -e + extents[1, i] < 0.0:
2359
+ return None
2360
+
2361
+ return (rayDir * tmin) + rayOrig, tmin
2362
+
2363
+
2364
+ def intersectRayTriangle(rayOrig, rayDir, tri, dtype=None):
2365
+ """Get the intersection of a ray and triangle(s).
2366
+
2367
+ This function can be used to achieve 'pixel-perfect' ray picking/casting on
2368
+ meshes defined with triangles. However, high-poly meshes may lead to
2369
+ performance issues.
2370
+
2371
+ Parameters
2372
+ ----------
2373
+ rayOrig : array_like
2374
+ Origin of the ray in space [x, y, z].
2375
+ rayDir : array_like
2376
+ Direction vector of the ray [x, y, z], should be normalized.
2377
+ tri : array_like
2378
+ Triangle vertices as 2D (3x3) array [p0, p1, p2] where each vertex is a
2379
+ length 3 array [vx, xy, vz]. The input array can be 3D (Nx3x3) to
2380
+ specify multiple triangles.
2381
+ dtype : dtype or str, optional
2382
+ Data type for computations can either be 'float32' or 'float64'. If
2383
+ `out` is specified, the data type of `out` is used and this argument is
2384
+ ignored. If `out` is not provided, 'float64' is used by default.
2385
+
2386
+ Returns
2387
+ -------
2388
+ tuple
2389
+ Coordinate in world space of the intersection, distance in scene
2390
+ units from `rayOrig`, and the barycentric coordinates on the triangle
2391
+ [x, y]. Returns `None` if there is no intersection.
2392
+
2393
+ """
2394
+ # based off `intersectRayTriangle` from GLM (https://glm.g-truc.net)
2395
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2396
+
2397
+ rayOrig = np.asarray(rayOrig, dtype=dtype)
2398
+ rayDir = np.asarray(rayDir, dtype=dtype)
2399
+ triVerts = np.asarray(tri, dtype=dtype)
2400
+
2401
+ edge1 = triVerts[1, :] - triVerts[0, :]
2402
+ edge2 = triVerts[2, :] - triVerts[0, :]
2403
+
2404
+ baryPos = np.zeros((2,), dtype=dtype)
2405
+
2406
+ p = np.cross(rayDir, edge2)
2407
+ det = np.dot(edge1, p)
2408
+
2409
+ if det > np.finfo(dtype).eps:
2410
+ dist = rayOrig - triVerts[0, :]
2411
+
2412
+ baryPos[0] = np.dot(dist, p)
2413
+ if baryPos[0] < 0.0 or baryPos[0] > det:
2414
+ return None
2415
+
2416
+ ortho = np.cross(dist, edge1)
2417
+
2418
+ baryPos[1] = np.dot(rayDir, ortho)
2419
+ if baryPos[1] < 0.0 or baryPos[0] + baryPos[1] > det:
2420
+ return None
2421
+
2422
+ elif det < -np.finfo(dtype).eps:
2423
+ dist = rayOrig - triVerts[0, :]
2424
+
2425
+ baryPos[0] = np.dot(dist, p)
2426
+ if baryPos[0] > 0.0 or baryPos[0] < det:
2427
+ return None
2428
+
2429
+ ortho = np.cross(dist, edge1)
2430
+
2431
+ baryPos[1] = np.dot(rayDir, ortho)
2432
+ if baryPos[1] > 0.0 or baryPos[0] + baryPos[1] < det:
2433
+ return None
2434
+ else:
2435
+ return None
2436
+
2437
+ invDet = 1.0 / det
2438
+ dist = np.dot(edge2, ortho) * invDet
2439
+ baryPos *= invDet
2440
+
2441
+ return (rayDir * dist) + rayOrig, dist, baryPos
2442
+
2443
+
2444
+ def ortho3Dto2D(p, orig, normal, up, right=None, dtype=None):
2445
+ """Get the planar coordinates of an orthogonal projection of a 3D point onto
2446
+ a 2D plane.
2447
+
2448
+ This function gets the nearest point on the plane which a 3D point falls on
2449
+ the plane.
2450
+
2451
+ Parameters
2452
+ ----------
2453
+ p : array_like
2454
+ Point to be projected on the plane.
2455
+ orig : array_like
2456
+ Origin of the plane to test [x, y, z].
2457
+ normal : array_like
2458
+ Normal vector of the plane [x, y, z], must be normalized.
2459
+ up : array_like
2460
+ Normalized up (+Y) direction of the plane's coordinate system. Must be
2461
+ perpendicular to `normal`.
2462
+ right : array_like, optional
2463
+ Perpendicular right (+X) axis. If not provided, the axis will be
2464
+ computed via the cross product between `normal` and `up`.
2465
+ dtype : dtype or str, optional
2466
+ Data type for computations can either be 'float32' or 'float64'. If
2467
+ `out` is specified, the data type of `out` is used and this argument is
2468
+ ignored. If `out` is not provided, 'float64' is used by default.
2469
+
2470
+ Returns
2471
+ -------
2472
+ ndarray
2473
+ Coordinates on the plane [X, Y] where the 3D point projects towards
2474
+ perpendicularly.
2475
+
2476
+ Examples
2477
+ --------
2478
+ This function can be used with :func:`intersectRayPlane` to find the
2479
+ location on the plane the ray intersects::
2480
+
2481
+ # plane information
2482
+ planeOrigin = [0, 0, 0]
2483
+ planeNormal = [0, 0, 1] # must be normalized
2484
+ planeUpAxis = perp([0, 1, 0], planeNormal) # must also be normalized
2485
+
2486
+ # ray
2487
+ rayDir = [0, 0, -1]
2488
+ rayOrigin = [0, 0, 5]
2489
+
2490
+ # get the intersect in 3D world space
2491
+ pnt = intersectRayPlane(rayOrigin, rayDir, planeOrigin, planeNormal)
2492
+
2493
+ # get the 2D coordinates on the plane the intersect occurred
2494
+ planeX, planeY = ortho3Dto2D(pnt, planeOrigin, planeNormal, planeUpAxis)
2495
+
2496
+ """
2497
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2498
+
2499
+ p = np.asarray(p, dtype=dtype)
2500
+ orig = np.asarray(orig, dtype=dtype)
2501
+ normal = np.asarray(normal, dtype=dtype)
2502
+ up = np.asarray(up, dtype=dtype)
2503
+
2504
+ toReturn = np.zeros((2,))
2505
+
2506
+ offset = p - orig
2507
+ if right is None:
2508
+ # derive X axis with cross product
2509
+ toReturn[0] = dot(offset, cross(normal, up, dtype=dtype), dtype=dtype)
2510
+ else:
2511
+ toReturn[0] = dot(offset, np.asarray(right, dtype=dtype), dtype=dtype)
2512
+
2513
+ toReturn[1] = dot(offset, up)
2514
+
2515
+ return toReturn
2516
+
2517
+
2518
+ def articulate(boneVecs, boneOris, dtype=None):
2519
+ """Articulate an armature.
2520
+
2521
+ This function is used for forward kinematics and posing by specifying a list
2522
+ of 'bones'. A bone has a length and orientation, where sequential bones are
2523
+ linked end-to-end. Returns the transformed origins of the bones in scene
2524
+ coordinates and their orientations.
2525
+
2526
+ There are many applications for forward kinematics such as posing armatures
2527
+ and stimuli for display (eg. mocap data). Another application is for getting
2528
+ the location of the end effector of coordinate measuring hardware, where
2529
+ encoders measure the joint angles and the length of linking members are
2530
+ known. This can be used for computing pose from "Sword of Damocles"[1]_ like
2531
+ hardware or some other haptic input devices which the participant wears (eg.
2532
+ a glove that measures joint angles in the hand). The computed pose of the
2533
+ joints can be used to interact with virtual stimuli.
2534
+
2535
+ Parameters
2536
+ ----------
2537
+ boneVecs : array_like
2538
+ Bone lengths [x, y, z] as an Nx3 array.
2539
+ boneOris : array_like
2540
+ Orientation of the bones as quaternions in form [x, y, z, w], relative
2541
+ to the previous bone.
2542
+ dtype : dtype or str, optional
2543
+ Data type for computations can either be 'float32' or 'float64'. If
2544
+ `out` is specified, the data type of `out` is used and this argument is
2545
+ ignored. If `out` is not provided, 'float64' is used by default.
2546
+
2547
+ Returns
2548
+ -------
2549
+ tuple
2550
+ Array of bone origins and orientations. The first origin is root
2551
+ position which is always at [0, 0, 0]. Use :func:`transform` to
2552
+ reposition the armature, or create a transformation matrix and use
2553
+ `applyMatrix` to translate and rotate the whole armature into position.
2554
+
2555
+ References
2556
+ ----------
2557
+ .. [1] Sutherland, I. E. (1968). "A head-mounted three dimensional display".
2558
+ Proceedings of AFIPS 68, pp. 757-764
2559
+
2560
+ Examples
2561
+ --------
2562
+ Compute the orientations and origins of segments of an arm::
2563
+
2564
+ # bone lengths
2565
+ boneLengths = [[0., 1., 0.], [0., 1., 0.], [0., 1., 0.]]
2566
+
2567
+ # create quaternions for joints
2568
+ shoulder = mt.quatFromAxisAngle('-y', 45.0)
2569
+ elbow = mt.quatFromAxisAngle('+z', 45.0)
2570
+ wrist = mt.quatFromAxisAngle('+z', 45.0)
2571
+
2572
+ # articulate the parts of the arm
2573
+ boxPos, boxOri = mt.articulate(pos, [shoulder, elbow, wrist])
2574
+
2575
+ # assign positions and orientations to 3D objects
2576
+ shoulderModel.thePose.posOri = (boxPos[0, :], boxOri[0, :])
2577
+ elbowModel.thePose.posOri = (boxPos[1, :], boxOri[1, :])
2578
+ wristModel.thePose.posOri = (boxPos[2, :], boxOri[2, :])
2579
+
2580
+ """
2581
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2582
+
2583
+ boneVecs = np.asarray(boneVecs, dtype=dtype)
2584
+ boneOris = np.asarray(boneOris, dtype=dtype)
2585
+
2586
+ jointOri = accumQuat(boneOris, dtype=dtype) # get joint orientations
2587
+ bonesRotated = applyQuat(jointOri, boneVecs, dtype=dtype) # rotate bones
2588
+
2589
+ # accumulate
2590
+ bonesTranslated = np.asarray(
2591
+ tuple(itertools.accumulate(bonesRotated[:], lambda a, b: a + b)),
2592
+ dtype=dtype)
2593
+ bonesTranslated -= bonesTranslated[0, :] # offset root length
2594
+
2595
+ return bonesTranslated, jointOri
2596
+
2597
+
2598
+ # ------------------------------------------------------------------------------
2599
+ # Quaternion Operations
2600
+ #
2601
+
2602
+ def slerp(q0, q1, t, shortest=True, out=None, dtype=None):
2603
+ """Spherical linear interpolation (SLERP) between two quaternions.
2604
+
2605
+ The behaviour of this function depends on the types of arguments:
2606
+
2607
+ * If `q0` and `q1` are both 1-D and `t` is scalar, the interpolation at `t`
2608
+ is returned.
2609
+ * If `q0` and `q1` are both 2-D Nx4 arrays and `t` is scalar, an Nx4 array
2610
+ is returned with each row containing the interpolation at `t` for each
2611
+ quaternion pair at matching row indices in `q0` and `q1`.
2612
+
2613
+ Parameters
2614
+ ----------
2615
+ q0 : array_like
2616
+ Initial quaternion in form [x, y, z, w] where w is real and x, y, z
2617
+ are imaginary components.
2618
+ q1 : array_like
2619
+ Final quaternion in form [x, y, z, w] where w is real and x, y, z
2620
+ are imaginary components.
2621
+ t : float
2622
+ Interpolation weight factor within interval 0.0 and 1.0.
2623
+ shortest : bool, optional
2624
+ Ensure interpolation occurs along the shortest arc along the 4-D
2625
+ hypersphere (default is `True`).
2626
+ out : ndarray, optional
2627
+ Optional output array. Must be same `shape` and `dtype` as the expected
2628
+ output if `out` was not specified.
2629
+ dtype : dtype or str, optional
2630
+ Data type for computations can either be 'float32' or 'float64'. If
2631
+ `out` is specified, the data type of `out` is used and this argument is
2632
+ ignored. If `out` is not provided, 'float64' is used by default.
2633
+
2634
+ Returns
2635
+ -------
2636
+ ndarray
2637
+ Quaternion [x, y, z, w] at `t`.
2638
+
2639
+ Examples
2640
+ --------
2641
+ Interpolate between two orientations::
2642
+
2643
+ q0 = quatFromAxisAngle(90.0, degrees=True)
2644
+ q1 = quatFromAxisAngle(-90.0, degrees=True)
2645
+ # halfway between 90 and -90 is 0.0 or quaternion [0. 0. 0. 1.]
2646
+ qr = slerp(q0, q1, 0.5)
2647
+
2648
+ Example of smooth rotation of an object with fixed angular velocity::
2649
+
2650
+ degPerSec = 10.0 # rotate a stimulus at 10 degrees per second
2651
+
2652
+ # initial orientation, axis rotates in the Z direction
2653
+ qr = quatFromAxisAngle([0., 0., -1.], 0.0, degrees=True)
2654
+ # amount to rotate every second
2655
+ qv = quatFromAxisAngle([0., 0., -1.], degPerSec, degrees=True)
2656
+
2657
+ # ---- within main experiment loop ----
2658
+ # `frameTime` is the time elapsed in seconds from last `slerp`.
2659
+ qr = multQuat(qr, slerp((0., 0., 0., 1.), qv, degPerSec * frameTime))
2660
+ _, angle = quatToAxisAngle(qr) # discard axis, only need angle
2661
+
2662
+ # myStim is a GratingStim or anything with an 'ori' argument which
2663
+ # accepts angle in degrees
2664
+ myStim.ori = angle
2665
+ myStim.draw()
2666
+
2667
+ """
2668
+ # Implementation based on code found here:
2669
+ # https://en.wikipedia.org/wiki/Slerp
2670
+ #
2671
+ if out is None:
2672
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2673
+ else:
2674
+ dtype = np.dtype(out.dtype).type
2675
+
2676
+ q0 = normalize(q0, dtype=dtype)
2677
+ q1 = normalize(q1, dtype=dtype)
2678
+ assert q0.shape == q1.shape
2679
+
2680
+ toReturn = np.zeros(q0.shape, dtype=dtype) if out is None else out
2681
+ toReturn.fill(0.0)
2682
+ t = dtype(t)
2683
+ q0, q1, qr = np.atleast_2d(q0, q1, toReturn)
2684
+
2685
+ d = np.clip(np.sum(q0 * q1, axis=1), -1.0, 1.0)
2686
+ if shortest:
2687
+ d[d < 0.0] *= -1.0
2688
+ q1[d < 0.0] *= -1.0
2689
+
2690
+ theta0 = np.arccos(d)
2691
+ theta = theta0 * t
2692
+ sinTheta = np.sin(theta)
2693
+ s1 = sinTheta / np.sin(theta0)
2694
+ s0 = np.cos(theta[:, np.newaxis]) - d[:, np.newaxis] * s1[:, np.newaxis]
2695
+ qr[:, :] = q0 * s0
2696
+ qr[:, :] += q1 * s1[:, np.newaxis]
2697
+ qr[:, :] += 0.0
2698
+
2699
+ return toReturn
2700
+
2701
+
2702
+ def quatToAxisAngle(q, degrees=True, dtype=None):
2703
+ """Convert a quaternion to `axis` and `angle` representation.
2704
+
2705
+ This allows you to use quaternions to set the orientation of stimuli that
2706
+ have an `ori` property.
2707
+
2708
+ Parameters
2709
+ ----------
2710
+ q : tuple, list or ndarray of float
2711
+ Quaternion in form [x, y, z, w] where w is real and x, y, z
2712
+ are imaginary components.
2713
+ degrees : bool, optional
2714
+ Indicate `angle` is to be returned in degrees, otherwise `angle` will be
2715
+ returned in radians.
2716
+ dtype : dtype or str, optional
2717
+ Data type for computations can either be 'float32' or 'float64'. If
2718
+ `out` is specified, the data type of `out` is used and this argument is
2719
+ ignored. If `out` is not provided, 'float64' is used by default.
2720
+
2721
+ Returns
2722
+ -------
2723
+ tuple
2724
+ Axis and angle of quaternion in form ([ax, ay, az], angle). If `degrees`
2725
+ is `True`, the angle returned is in degrees, radians if `False`.
2726
+
2727
+ Examples
2728
+ --------
2729
+ Using a quaternion to rotate a stimulus a fixed angle each frame::
2730
+
2731
+ # initial orientation, axis rotates in the Z direction
2732
+ qr = quatFromAxisAngle([0., 0., -1.], 0.0, degrees=True)
2733
+ # rotation per-frame, here it's 0.1 degrees per frame
2734
+ qf = quatFromAxisAngle([0., 0., -1.], 0.1, degrees=True)
2735
+
2736
+ # ---- within main experiment loop ----
2737
+ # myStim is a GratingStim or anything with an 'ori' argument which
2738
+ # accepts angle in degrees
2739
+ qr = multQuat(qr, qf) # cumulative rotation
2740
+ _, angle = quatToAxisAngle(qr) # discard axis, only need angle
2741
+ myStim.ori = angle
2742
+ myStim.draw()
2743
+
2744
+ """
2745
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2746
+ q = normalize(q, dtype=dtype) # returns ndarray
2747
+ v = np.sqrt(np.sum(np.square(q[:3])))
2748
+
2749
+ if np.count_nonzero(q[:3]):
2750
+ axis = q[:3] / v
2751
+ angle = dtype(2.0) * np.arctan2(v, q[3])
2752
+ else:
2753
+ axis = np.zeros((3,), dtype=dtype)
2754
+ axis[0] = 1.
2755
+ angle = 0.0
2756
+
2757
+ axis += 0.0
2758
+
2759
+ return axis, np.degrees(angle) if degrees else angle
2760
+
2761
+
2762
+ def quatFromAxisAngle(axis, angle, degrees=True, dtype=None):
2763
+ """Create a quaternion to represent a rotation about `axis` vector by
2764
+ `angle`.
2765
+
2766
+ Parameters
2767
+ ----------
2768
+ axis : tuple, list, ndarray or str
2769
+ Axis vector components or axis name. If a vector, input must be length
2770
+ 3 [x, y, z]. A string can be specified for rotations about world axes
2771
+ (eg. `'+x'`, `'-z'`, `'+y'`, etc.)
2772
+ angle : float
2773
+ Rotation angle in radians (or degrees if `degrees` is `True`. Rotations
2774
+ are right-handed about the specified `axis`.
2775
+ degrees : bool, optional
2776
+ Indicate `angle` is in degrees, otherwise `angle` will be treated as
2777
+ radians.
2778
+ dtype : dtype or str, optional
2779
+ Data type for computations can either be 'float32' or 'float64'. If
2780
+ `out` is specified, the data type of `out` is used and this argument is
2781
+ ignored. If `out` is not provided, 'float64' is used by default.
2782
+
2783
+ Returns
2784
+ -------
2785
+ ndarray
2786
+ Quaternion [x, y, z, w].
2787
+
2788
+ Examples
2789
+ --------
2790
+ Create a quaternion from specified `axis` and `angle`::
2791
+
2792
+ axis = [0., 0., -1.] # rotate about -Z axis
2793
+ angle = 90.0 # angle in degrees
2794
+ ori = quatFromAxisAngle(axis, angle, degrees=True) # using degrees!
2795
+
2796
+ """
2797
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2798
+ toReturn = np.zeros((4,), dtype=dtype)
2799
+
2800
+ if degrees:
2801
+ halfRad = np.radians(angle, dtype=dtype) / dtype(2.0)
2802
+ else:
2803
+ halfRad = np.dtype(dtype).type(angle) / dtype(2.0)
2804
+
2805
+ try:
2806
+ axis = VEC_AXES[axis] if isinstance(axis, str) else axis
2807
+ except KeyError:
2808
+ raise ValueError(
2809
+ "Value of `axis` must be either '+X', '-X', '+Y', '-Y', '+Z' or "
2810
+ "'-Z' or length 3 vector.")
2811
+
2812
+ axis = normalize(axis, dtype=dtype)
2813
+ if np.count_nonzero(axis) == 0:
2814
+ raise ValueError("Value for `axis` is zero-length.")
2815
+
2816
+ np.multiply(axis, np.sin(halfRad), out=toReturn[:3])
2817
+ toReturn[3] = np.cos(halfRad)
2818
+ toReturn += 0.0 # remove negative zeros
2819
+
2820
+ return toReturn
2821
+
2822
+
2823
+ def quatYawPitchRoll(q, degrees=True, out=None, dtype=None):
2824
+ """Get the yaw, pitch, and roll of a quaternion's orientation relative to
2825
+ the world -Z axis.
2826
+
2827
+ You can multiply the quaternion by the inverse of some other one to make the
2828
+ returned values referenced to a local coordinate system.
2829
+
2830
+ Parameters
2831
+ ----------
2832
+ q : tuple, list or ndarray of float
2833
+ Quaternion in form [x, y, z, w] where w is real and x, y, z
2834
+ are imaginary components.
2835
+ degrees : bool, optional
2836
+ Indicate angles are to be returned in degrees, otherwise they will be
2837
+ returned in radians.
2838
+ out : ndarray
2839
+ Optional output array. Must have same `shape` and `dtype` as what is
2840
+ expected to be returned by this function of `out` was not specified.
2841
+ dtype : dtype or str, optional
2842
+ Data type for computations can either be 'float32' or 'float64'. If
2843
+ `out` is specified, the data type of `out` is used and this argument is
2844
+ ignored. If `out` is not provided, 'float64' is used by default.
2845
+
2846
+ Returns
2847
+ -------
2848
+ ndarray
2849
+ Yaw, pitch and roll [yaw, pitch, roll] of quaternion `q`.
2850
+
2851
+ """
2852
+ # based off code found here:
2853
+ # https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
2854
+ # Yields the same results as PsychXR's LibOVRPose.getYawPitchRoll method.
2855
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2856
+ q = np.asarray(q, dtype=dtype)
2857
+
2858
+ toReturn = np.zeros((3,), dtype=dtype) if out is None else out
2859
+
2860
+ sinRcosP = 2.0 * (q[3] * q[0] + q[1] * q[2])
2861
+ cosRcosP = 1.0 - 2.0 * (q[0] * q[0] + q[1] * q[1])
2862
+
2863
+ toReturn[0] = np.arctan2(sinRcosP, cosRcosP)
2864
+
2865
+ sinp = 2.0 * (q[3] * q[1] - q[2] * q[0])
2866
+
2867
+ if np.fabs(sinp) >= 1.:
2868
+ toReturn[1] = np.copysign(np.pi / 2., sinp)
2869
+ else:
2870
+ toReturn[1] = np.arcsin(sinp)
2871
+
2872
+ sinYcosP = 2.0 * (q[3] * q[2] + q[0] * q[1])
2873
+ cosYcosP = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2])
2874
+
2875
+ toReturn[2] = np.arctan2(sinYcosP, cosYcosP)
2876
+
2877
+ if degrees:
2878
+ toReturn[:] = np.degrees(toReturn[:])
2879
+
2880
+ return toReturn
2881
+
2882
+
2883
+ def quatMagnitude(q, squared=False, out=None, dtype=None):
2884
+ """Get the magnitude of a quaternion.
2885
+
2886
+ A quaternion is normalized if its magnitude is 1.
2887
+
2888
+ Parameters
2889
+ ----------
2890
+ q : array_like
2891
+ Quaternion(s) in form [x, y, z, w] where w is real and x, y, z are
2892
+ imaginary components.
2893
+ squared : bool, optional
2894
+ If ``True`` return the squared magnitude. If you are just checking if a
2895
+ quaternion is normalized, the squared magnitude will suffice to avoid
2896
+ the square root operation.
2897
+ out : ndarray, optional
2898
+ Optional output array. Must be same `shape` and `dtype` as the expected
2899
+ output if `out` was not specified.
2900
+ dtype : dtype or str, optional
2901
+ Data type for computations can either be 'float32' or 'float64'. If
2902
+ `out` is specified, the data type of `out` is used and this argument is
2903
+ ignored. If `out` is not provided, 'float64' is used by default.
2904
+
2905
+ Returns
2906
+ -------
2907
+ float or ndarray
2908
+ Magnitude of quaternion `q`.
2909
+
2910
+ """
2911
+ if out is None:
2912
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2913
+ else:
2914
+ dtype = np.dtype(out.dtype).type
2915
+
2916
+ q = np.asarray(q, dtype=dtype)
2917
+ if q.ndim == 1:
2918
+ assert q.shape[0] == 4
2919
+ if squared:
2920
+ toReturn = np.sum(np.square(q))
2921
+ else:
2922
+ toReturn = np.sqrt(np.sum(np.square(q)))
2923
+ elif q.ndim == 2:
2924
+ assert q.shape[1] == 4
2925
+ toReturn = np.zeros((q.shape[0],), dtype=dtype) if out is None else out
2926
+ if squared:
2927
+ toReturn[:] = np.sum(np.square(q), axis=1)
2928
+ else:
2929
+ toReturn[:] = np.sqrt(np.sum(np.square(q), axis=1))
2930
+ else:
2931
+ raise ValueError("Input argument 'q' has incorrect dimensions.")
2932
+
2933
+ return toReturn
2934
+
2935
+
2936
+ def multQuat(q0, q1, out=None, dtype=None):
2937
+ """Multiply quaternion `q0` and `q1`.
2938
+
2939
+ The orientation of the returned quaternion is the combination of the input
2940
+ quaternions.
2941
+
2942
+ Parameters
2943
+ ----------
2944
+ q0, q1 : array_like
2945
+ Quaternions to multiply in form [x, y, z, w] where w is real and x, y, z
2946
+ are imaginary components. If 2D (Nx4) arrays are specified, quaternions
2947
+ are multiplied row-wise between each array.
2948
+ out : ndarray, optional
2949
+ Optional output array. Must be same `shape` and `dtype` as the expected
2950
+ output if `out` was not specified.
2951
+ dtype : dtype or str, optional
2952
+ Data type for computations can either be 'float32' or 'float64'. If
2953
+ `out` is specified, the data type of `out` is used and this argument is
2954
+ ignored. If `out` is not provided, 'float64' is used by default.
2955
+
2956
+ Returns
2957
+ -------
2958
+ ndarray
2959
+ Combined orientations of `q0` amd `q1`.
2960
+
2961
+ Notes
2962
+ -----
2963
+ * Quaternions are normalized prior to multiplication.
2964
+
2965
+ Examples
2966
+ --------
2967
+ Combine the orientations of two quaternions::
2968
+
2969
+ a = quatFromAxisAngle([0, 0, -1], 45.0, degrees=True)
2970
+ b = quatFromAxisAngle([0, 0, -1], 90.0, degrees=True)
2971
+ c = multQuat(a, b) # rotates 135 degrees about -Z axis
2972
+
2973
+ """
2974
+ if out is None:
2975
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2976
+ else:
2977
+ dtype = np.dtype(out.dtype).type
2978
+
2979
+ q0 = normalize(q0, dtype=dtype)
2980
+ q1 = normalize(q1, dtype=dtype)
2981
+ assert q0.shape == q1.shape
2982
+ toReturn = np.zeros(q0.shape, dtype=dtype) if out is None else out
2983
+ toReturn.fill(0.0) # clear array
2984
+ q0, q1, qr = np.atleast_2d(q0, q1, toReturn)
2985
+
2986
+ # multiply quaternions for each row of the operand arrays
2987
+ qr[:, :3] = np.cross(q0[:, :3], q1[:, :3], axis=1)
2988
+ qr[:, :3] += q0[:, :3] * np.expand_dims(q1[:, 3], axis=1)
2989
+ qr[:, :3] += q1[:, :3] * np.expand_dims(q0[:, 3], axis=1)
2990
+ qr[:, 3] = q0[:, 3]
2991
+ qr[:, 3] *= q1[:, 3]
2992
+ qr[:, 3] -= np.sum(np.multiply(q0[:, :3], q1[:, :3]), axis=1) # dot product
2993
+ qr += 0.0
2994
+
2995
+ return toReturn
2996
+
2997
+
2998
+ def invertQuat(q, out=None, dtype=None):
2999
+ """Get the multiplicative inverse of a quaternion.
3000
+
3001
+ This gives a quaternion which rotates in the opposite direction with equal
3002
+ magnitude. Multiplying a quaternion by its inverse returns an identity
3003
+ quaternion as both orientations cancel out.
3004
+
3005
+ Parameters
3006
+ ----------
3007
+ q : ndarray, list, or tuple of float
3008
+ Quaternion to invert in form [x, y, z, w] where w is real and x, y, z
3009
+ are imaginary components. If `q` is 2D (Nx4), each row is treated as a
3010
+ separate quaternion and inverted.
3011
+ out : ndarray, optional
3012
+ Optional output array. Must be same `shape` and `dtype` as the expected
3013
+ output if `out` was not specified.
3014
+ dtype : dtype or str, optional
3015
+ Data type for computations can either be 'float32' or 'float64'. If
3016
+ `out` is specified, the data type of `out` is used and this argument is
3017
+ ignored. If `out` is not provided, 'float64' is used by default.
3018
+
3019
+ Returns
3020
+ -------
3021
+ ndarray
3022
+ Inverse of quaternion `q`.
3023
+
3024
+ Examples
3025
+ --------
3026
+ Show that multiplying a quaternion by its inverse returns an identity
3027
+ quaternion where [x=0, y=0, z=0, w=1]::
3028
+
3029
+ angle = 90.0
3030
+ axis = [0., 0., -1.]
3031
+ q = quatFromAxisAngle(axis, angle, degrees=True)
3032
+ qinv = invertQuat(q)
3033
+ qr = multQuat(q, qinv)
3034
+ qi = np.array([0., 0., 0., 1.]) # identity quaternion
3035
+ print(np.allclose(qi, qr)) # True
3036
+
3037
+ Notes
3038
+ -----
3039
+ * Quaternions are normalized prior to inverting.
3040
+
3041
+ """
3042
+ if out is None:
3043
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3044
+ else:
3045
+ dtype = np.dtype(out.dtype).type
3046
+
3047
+ q = normalize(q, dtype=dtype)
3048
+ toReturn = np.zeros(q.shape, dtype=dtype) if out is None else out
3049
+ qn, qinv = np.atleast_2d(q, toReturn) # 2d views
3050
+
3051
+ # conjugate the quaternion
3052
+ qinv[:, :3] = -qn[:, :3]
3053
+ qinv[:, 3] = qn[:, 3]
3054
+ qinv /= np.sum(np.square(qn), axis=1)[:, np.newaxis]
3055
+ qinv += 0.0 # remove negative zeros
3056
+
3057
+ return toReturn
3058
+
3059
+
3060
+ def applyQuat(q, points, out=None, dtype=None):
3061
+ """Rotate points/coordinates using a quaternion.
3062
+
3063
+ This is similar to using `applyMatrix` with a rotation matrix. However, it
3064
+ is computationally less intensive to use `applyQuat` if one only wishes to
3065
+ rotate points.
3066
+
3067
+ Parameters
3068
+ ----------
3069
+ q : array_like
3070
+ Quaternion to invert in form [x, y, z, w] where w is real and x, y, z
3071
+ are imaginary components.
3072
+ points : array_like
3073
+ 2D array of vectors or points to transform, where each row is a single
3074
+ point. Only the x, y, and z components (the first three columns) are
3075
+ rotated. Additional columns are copied.
3076
+ out : ndarray, optional
3077
+ Optional output array. Must be same `shape` and `dtype` as the expected
3078
+ output if `out` was not specified.
3079
+ dtype : dtype or str, optional
3080
+ Data type for computations can either be 'float32' or 'float64'. If
3081
+ `out` is specified, the data type of `out` is used and this argument is
3082
+ ignored. If `out` is not provided, 'float64' is used by default.
3083
+
3084
+ Returns
3085
+ -------
3086
+ ndarray
3087
+ Transformed points.
3088
+
3089
+ Examples
3090
+ --------
3091
+ Rotate points using a quaternion::
3092
+
3093
+ points = [[1., 0., 0.], [0., -1., 0.]]
3094
+ quat = quatFromAxisAngle(-90.0, [0., 0., -1.], degrees=True)
3095
+ pointsRotated = applyQuat(quat, points)
3096
+ # [[0. 1. 0.]
3097
+ # [1. 0. 0.]]
3098
+
3099
+ Show that you get the same result as a rotation matrix::
3100
+
3101
+ axis = [0., 0., -1.]
3102
+ angle = -90.0
3103
+ rotMat = rotationMatrix(axis, angle)[:3, :3] # rotation sub-matrix only
3104
+ rotQuat = quatFromAxisAngle(angle, axis, degrees=True)
3105
+ points = [[1., 0., 0.], [0., -1., 0.]]
3106
+ isClose = np.allclose(applyMatrix(rotMat, points), # True
3107
+ applyQuat(rotQuat, points))
3108
+
3109
+ Specifying an array to `q` where each row is a quaternion transforms points
3110
+ in corresponding rows of `points`::
3111
+
3112
+ points = [[1., 0., 0.], [0., -1., 0.]]
3113
+ quats = [quatFromAxisAngle(-90.0, [0., 0., -1.], degrees=True),
3114
+ quatFromAxisAngle(45.0, [0., 0., -1.], degrees=True)]
3115
+ applyQuat(quats, points)
3116
+
3117
+ """
3118
+ # based on 'quat_mul_vec3' implementation from linmath.h
3119
+ if out is None:
3120
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3121
+ else:
3122
+ dtype = np.dtype(out.dtype).type
3123
+
3124
+ qin = np.asarray(q, dtype=dtype)
3125
+ points = np.asarray(points, dtype=dtype)
3126
+
3127
+ if out is not None:
3128
+ assert points.shape == out.shape
3129
+
3130
+ toReturn = np.zeros(points.shape, dtype=dtype) if out is None else out
3131
+ pin, pout = np.atleast_2d(points, toReturn)
3132
+ pout[:, :] = pin[:, :] # copy values into output array
3133
+
3134
+ if qin.ndim == 1:
3135
+ assert qin.shape[0] == 4
3136
+ t = cross(qin[:3], pin[:, :3]) * dtype(2.0)
3137
+ u = cross(qin[:3], t)
3138
+ t *= qin[3]
3139
+ pout[:, :3] += t
3140
+ pout[:, :3] += u
3141
+ elif qin.ndim == 2:
3142
+ assert qin.shape[1] == 4 and qin.shape[0] == pin.shape[0]
3143
+ t = cross(qin[:, :3], pin[:, :3])
3144
+ t *= dtype(2.0)
3145
+ u = cross(qin[:, :3], t)
3146
+ t *= np.expand_dims(qin[:, 3], axis=1)
3147
+ pout[:, :3] += t
3148
+ pout[:, :3] += u
3149
+ else:
3150
+ raise ValueError("Input arguments have invalid dimensions.")
3151
+
3152
+ return toReturn
3153
+
3154
+
3155
+ def accumQuat(qlist, out=None, dtype=None):
3156
+ """Accumulate quaternion rotations.
3157
+
3158
+ Chain multiplies an Nx4 array of quaternions, accumulating their rotations.
3159
+ This function can be used for computing the orientation of joints in an
3160
+ armature for forward kinematics. The first quaternion is treated as the
3161
+ 'root' and the last is the orientation of the end effector.
3162
+
3163
+ Parameters
3164
+ ----------
3165
+ q : array_like
3166
+ Nx4 array of quaternions to accumulate, where each row is a quaternion.
3167
+ out : ndarray, optional
3168
+ Optional output array. Must be same `shape` and `dtype` as the expected
3169
+ output if `out` was not specified. In this case, the same shape as
3170
+ `qlist`.
3171
+ dtype : dtype or str, optional
3172
+ Data type for computations can either be 'float32' or 'float64'. If
3173
+ `out` is specified, the data type of `out` is used and this argument is
3174
+ ignored. If `out` is not provided, 'float64' is used by default.
3175
+
3176
+ Returns
3177
+ -------
3178
+ ndarray
3179
+ Nx4 array of quaternions.
3180
+
3181
+ Examples
3182
+ --------
3183
+ Get the orientation of joints in an armature if we know their relative
3184
+ angles::
3185
+
3186
+ shoulder = quatFromAxisAngle('-x', 45.0) # rotate shoulder down 45 deg
3187
+ elbow = quatFromAxisAngle('+x', 45.0) # rotate elbow up 45 deg
3188
+ wrist = quatFromAxisAngle('-x', 45.0) # rotate wrist down 45 deg
3189
+ finger = quatFromAxisAngle('+x', 0.0) # keep finger in-line with wrist
3190
+
3191
+ armRotations = accumQuat([shoulder, elbow, wrist, finger])
3192
+
3193
+ """
3194
+ if out is None:
3195
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3196
+ else:
3197
+ dtype = np.dtype(out.dtype).type
3198
+
3199
+ qlist = np.asarray(qlist, dtype=dtype)
3200
+ qlist = np.atleast_2d(qlist)
3201
+
3202
+ qr = np.zeros_like(qlist, dtype=dtype) if out is None else out
3203
+ qr[:, :] = tuple(itertools.accumulate(
3204
+ qlist[:], lambda a, b: multQuat(a, b, dtype=dtype)))
3205
+
3206
+ return qr
3207
+
3208
+
3209
+ def alignTo(v, t, out=None, dtype=None):
3210
+ """Compute a quaternion which rotates one vector to align with another.
3211
+
3212
+ Parameters
3213
+ ----------
3214
+ v : array_like
3215
+ Vector [x, y, z] to rotate. Can be Nx3, but must have the same shape as
3216
+ `t`.
3217
+ t : array_like
3218
+ Target [x, y, z] vector to align to. Can be Nx3, but must have the same
3219
+ shape as `v`.
3220
+ out : ndarray, optional
3221
+ Optional output array. Must be same `shape` and `dtype` as the expected
3222
+ output if `out` was not specified.
3223
+ dtype : dtype or str, optional
3224
+ Data type for computations can either be 'float32' or 'float64'. If
3225
+ `out` is specified, the data type of `out` is used and this argument is
3226
+ ignored. If `out` is not provided, 'float64' is used by default.
3227
+
3228
+ Returns
3229
+ -------
3230
+ ndarray
3231
+ Quaternion which rotates `v` to `t`.
3232
+
3233
+ Examples
3234
+ --------
3235
+ Rotate some vectors to align with other vectors, inputs should be
3236
+ normalized::
3237
+
3238
+ vec = [[1, 0, 0], [0, 1, 0], [1, 0, 0]]
3239
+ targets = [[0, 1, 0], [0, -1, 0], [-1, 0, 0]]
3240
+
3241
+ qr = alignTo(vec, targets)
3242
+ vecRotated = applyQuat(qr, vec)
3243
+
3244
+ numpy.allclose(vecRotated, targets) # True
3245
+
3246
+ Get matrix which orients vertices towards a point::
3247
+
3248
+ point = [5, 6, 7]
3249
+ vec = [0, 0, -1] # initial facing is -Z (forward in GL)
3250
+
3251
+ targetVec = normalize(point - vec)
3252
+ qr = alignTo(vec, targetVec) # get rotation to align
3253
+
3254
+ M = quatToMatrix(qr) # 4x4 transformation matrix
3255
+
3256
+ """
3257
+ # based off Quaternion::align from Quaternion.hpp from OpenMP
3258
+ if out is None:
3259
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3260
+ else:
3261
+ dtype = np.dtype(out.dtype).type
3262
+
3263
+ v = normalize(v, dtype=dtype)
3264
+ t = normalize(t, dtype=dtype)
3265
+
3266
+ if out is None:
3267
+ if v.ndim == 1:
3268
+ toReturn = np.zeros((4,), dtype=dtype)
3269
+ else:
3270
+ toReturn = np.zeros((v.shape[0], 4), dtype=dtype)
3271
+ else:
3272
+ toReturn = out
3273
+
3274
+ qr, v2d, t2d = np.atleast_2d(toReturn, v, t)
3275
+
3276
+ b = bisector(v2d, t2d, norm=True, dtype=dtype)
3277
+ cosHalfAngle = dot(v2d, b, dtype=dtype)
3278
+
3279
+ nonparallel = cosHalfAngle > 0.0 # rotation is not 180 degrees
3280
+ qr[nonparallel, :3] = cross(v2d[nonparallel], b[nonparallel], dtype=dtype)
3281
+ qr[nonparallel, 3] = cosHalfAngle[nonparallel]
3282
+
3283
+ if np.all(nonparallel): # don't bother handling special cases
3284
+ return toReturn + 0.0
3285
+
3286
+ # deal with cases where the vectors are facing exact opposite directions
3287
+ ry = np.logical_and(np.abs(v2d[:, 0]) >= np.abs(v2d[:, 1]), ~nonparallel)
3288
+ rx = np.logical_and(~ry, ~nonparallel)
3289
+
3290
+ getLength = lambda x, y: np.sqrt(x * x + y * y)
3291
+ if not np.all(rx):
3292
+ invLength = getLength(v2d[ry, 0], v2d[ry, 2])
3293
+ invLength = np.where(invLength > 0.0, 1.0 / invLength, invLength) # avoid x / 0
3294
+ qr[ry, 0] = -v2d[ry, 2] * invLength
3295
+ qr[ry, 2] = v2d[ry, 0] * invLength
3296
+
3297
+ if not np.all(ry): # skip if all the same edge case
3298
+ invLength = getLength(v2d[rx, 1], v2d[rx, 2])
3299
+ invLength = np.where(invLength > 0.0, 1.0 / invLength, invLength)
3300
+ qr[rx, 1] = v2d[rx, 2] * invLength
3301
+ qr[rx, 2] = -v2d[rx, 1] * invLength
3302
+
3303
+ return toReturn + 0.0
3304
+
3305
+
3306
+ def matrixToQuat(m, out=None, dtype=None):
3307
+ """Convert a rotation matrix to a quaternion.
3308
+
3309
+ Parameters
3310
+ ----------
3311
+ m : array_like
3312
+ 3x3 rotation matrix (row-major). A 4x4 affine transformation matrix may
3313
+ be provided, assuming the top-left 3x3 sub-matrix is orthonormal and
3314
+ is a rotation group.
3315
+ out : ndarray, optional
3316
+ Optional output array. Must be same `shape` and `dtype` as the expected
3317
+ output if `out` was not specified.
3318
+ dtype : dtype or str, optional
3319
+ Data type for computations can either be 'float32' or 'float64'. If
3320
+ `out` is specified, the data type of `out` is used and this argument is
3321
+ ignored. If `out` is not provided, 'float64' is used by default.
3322
+
3323
+ Returns
3324
+ -------
3325
+ ndarray
3326
+ Rotation quaternion.
3327
+
3328
+ Notes
3329
+ -----
3330
+ * Depending on the input, returned quaternions may not be exactly the same
3331
+ as the one used to construct the rotation matrix (i.e. by calling
3332
+ `quatToMatrix`), typically when a large rotation angle is used. However,
3333
+ the returned quaternion should result in the same rotation when applied to
3334
+ points.
3335
+
3336
+ Examples
3337
+ --------
3338
+ Converting a rotation matrix from the OpenGL matrix stack to a quaternion::
3339
+
3340
+ glRotatef(45., -1, 0, 0)
3341
+
3342
+ m = np.zeros((4, 4), dtype='float32') # store the matrix
3343
+ GL.glGetFloatv(
3344
+ GL.GL_MODELVIEW_MATRIX,
3345
+ m.ctypes.data_as(ctypes.POINTER(ctypes.c_float)))
3346
+
3347
+ qr = matrixToQuat(m.T) # must be transposed
3348
+
3349
+ Interpolation between two 4x4 transformation matrices::
3350
+
3351
+ interpWeight = 0.5
3352
+
3353
+ posStart = mStart[:3, 3]
3354
+ oriStart = matrixToQuat(mStart)
3355
+
3356
+ posEnd = mEnd[:3, 3]
3357
+ oriEnd = matrixToQuat(mEnd)
3358
+
3359
+ oriInterp = slerp(qStart, qEnd, interpWeight)
3360
+ posInterp = lerp(posStart, posEnd, interpWeight)
3361
+
3362
+ mInterp = posOriToMatrix(posInterp, oriInterp)
3363
+
3364
+ """
3365
+ # based off example `Maths - Conversion Matrix to Quaternion` from
3366
+ # https://www.euclideanspace.com/
3367
+ if out is None:
3368
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3369
+ else:
3370
+ dtype = np.dtype(out.dtype).type
3371
+
3372
+ m = np.asarray(m, dtype=dtype)
3373
+
3374
+ if m.shape == (4, 4,) or m.shape == (3, 4,):
3375
+ m = m[:3, :3] # keep only rotation group sub-matrix
3376
+ elif m.shape == (3, 3,):
3377
+ pass # fine, nop
3378
+ else:
3379
+ raise ValueError("Input matrix `m` must be 3x3 or 4x4.")
3380
+
3381
+ toReturn = np.zeros((4,), dtype=dtype) if out is None else out
3382
+
3383
+ tr = m[0, 0] + m[1, 1] + m[2, 2]
3384
+ if tr > 0.0:
3385
+ s = np.sqrt(tr + 1.0) * 2.0
3386
+ toReturn[3] = dtype(0.25) * s
3387
+ toReturn[0] = (m[2, 1] - m[1, 2]) / s
3388
+ toReturn[1] = (m[0, 2] - m[2, 0]) / s
3389
+ toReturn[2] = (m[1, 0] - m[0, 1]) / s
3390
+ elif m[0, 0] > m[1, 1] and m[0, 0] > m[2, 2]:
3391
+ s = np.sqrt(dtype(1.0) + m[0, 0] - m[1, 1] - m[2, 2]) * dtype(2.0)
3392
+ toReturn[3] = (m[2, 1] - m[1, 2]) / s
3393
+ toReturn[0] = dtype(0.25) * s
3394
+ toReturn[1] = (m[0, 1] + m[1, 0]) / s
3395
+ toReturn[2] = (m[0, 2] + m[2, 0]) / s
3396
+ elif m[1, 1] > m[2, 2]:
3397
+ s = np.sqrt(dtype(1.0) + m[1, 1] - m[0, 0] - m[2, 2]) * dtype(2.0)
3398
+ toReturn[3] = (m[0, 2] - m[2, 0]) / s
3399
+ toReturn[0] = (m[0, 1] + m[1, 0]) / s
3400
+ toReturn[1] = dtype(0.25) * s
3401
+ toReturn[2] = (m[1, 2] + m[2, 1]) / s
3402
+ else:
3403
+ s = np.sqrt(dtype(1.0) + m[2, 2] - m[0, 0] - m[1, 1]) * dtype(2.0)
3404
+ toReturn[3] = (m[1, 0] - m[0, 1]) / s
3405
+ toReturn[0] = (m[0, 2] + m[2, 0]) / s
3406
+ toReturn[1] = (m[1, 2] + m[2, 1]) / s
3407
+ toReturn[2] = dtype(0.25) * s
3408
+
3409
+ return toReturn
3410
+
3411
+
3412
+ # ------------------------------------------------------------------------------
3413
+ # Matrix Operations
3414
+ #
3415
+
3416
+ def identityMatrix(size=4, out=None, dtype=None):
3417
+ """Create an sqaure identity matrix.
3418
+
3419
+ Parameters
3420
+ ----------
3421
+ size : int, optional
3422
+ Size of the matrix. Default is `4` for a 4x4 identity matrix.
3423
+ out : ndarray, optional
3424
+ Optional output array. Must be same `shape` and `dtype` as the expected
3425
+ output if `out` was not specified.
3426
+ dtype : dtype or str, optional
3427
+ Data type for computations can either be 'float32' or 'float64'. If
3428
+ `out` is specified, the data type of `out` is used and this argument is
3429
+ ignored. If `out` is not provided, 'float64' is used by default.
3430
+
3431
+ Returns
3432
+ -------
3433
+ ndarray
3434
+ Identity matrix.
3435
+
3436
+ """
3437
+ # this might seem overkill for creating an identity matrix, but it is
3438
+ # necessary to ensure the correct data type is used
3439
+ if out is None:
3440
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3441
+ ident = np.zeros((size, size), dtype=dtype)
3442
+ else:
3443
+ dtype = np.dtype(out.dtype).type
3444
+ ident = out
3445
+ ident.fill(0.0)
3446
+
3447
+ np.fill_diagonal(ident, 1.0)
3448
+
3449
+ return ident
3450
+
3451
+
3452
+ def quatToMatrix(q, out=None, dtype=None):
3453
+ """Create a 4x4 rotation matrix from a quaternion.
3454
+
3455
+ Parameters
3456
+ ----------
3457
+ q : tuple, list or ndarray of float
3458
+ Quaternion to convert in form [x, y, z, w] where w is real and x, y, z
3459
+ are imaginary components.
3460
+ out : ndarray or None
3461
+ Optional output array. Must be same `shape` and `dtype` as the expected
3462
+ output if `out` was not specified.
3463
+ dtype : dtype or str, optional
3464
+ Data type for computations can either be 'float32' or 'float64'. If
3465
+ `out` is specified, the data type of `out` is used and this argument is
3466
+ ignored. If `out` is not provided, 'float64' is used by default.
3467
+
3468
+ Returns
3469
+ -------
3470
+ ndarray or None
3471
+ 4x4 rotation matrix in row-major order.
3472
+
3473
+ Examples
3474
+ --------
3475
+ Convert a quaternion to a rotation matrix::
3476
+
3477
+ point = [0., 1., 0., 1.] # 4-vector form [x, y, z, 1.0]
3478
+ ori = [0., 0., 0., 1.]
3479
+ rotMat = quatToMatrix(ori)
3480
+ # rotate 'point' using matrix multiplication
3481
+ newPoint = np.matmul(rotMat.T, point) # returns [-1., 0., 0., 1.]
3482
+
3483
+ Rotate all points in an array (each row is a coordinate)::
3484
+
3485
+ points = np.asarray([[0., 0., 0., 1.],
3486
+ [0., 1., 0., 1.],
3487
+ [1., 1., 0., 1.]])
3488
+ newPoints = points.dot(rotMat)
3489
+
3490
+ Notes
3491
+ -----
3492
+ * Quaternions are normalized prior to conversion.
3493
+
3494
+ """
3495
+ # based off implementations from
3496
+ # https://github.com/glfw/glfw/blob/master/deps/linmath.h
3497
+ if out is None:
3498
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3499
+ R = np.zeros((4, 4,), dtype=dtype)
3500
+ else:
3501
+ dtype = np.dtype(out.dtype).type
3502
+ R = out
3503
+ R.fill(0.0)
3504
+
3505
+ q = normalize(q, dtype=dtype)
3506
+ b, c, d, a = q[:]
3507
+ vsqr = np.square(q)
3508
+
3509
+ u = dtype(2.0)
3510
+ R[0, 0] = vsqr[3] + vsqr[0] - vsqr[1] - vsqr[2]
3511
+ R[1, 0] = u * (b * c + a * d)
3512
+ R[2, 0] = u * (b * d - a * c)
3513
+
3514
+ R[0, 1] = u * (b * c - a * d)
3515
+ R[1, 1] = vsqr[3] - vsqr[0] + vsqr[1] - vsqr[2]
3516
+ R[2, 1] = u * (c * d + a * b)
3517
+
3518
+ R[0, 2] = u * (b * d + a * c)
3519
+ R[1, 2] = u * (c * d - a * b)
3520
+ R[2, 2] = vsqr[3] - vsqr[0] - vsqr[1] + vsqr[2]
3521
+
3522
+ R[3, 3] = dtype(1.0)
3523
+ R[:, :] += 0.0 # remove negative zeros
3524
+
3525
+ return R
3526
+
3527
+
3528
+ def scaleMatrix(s, out=None, dtype=None):
3529
+ """Create a scaling matrix.
3530
+
3531
+ The resulting matrix is the same as a generated by a `glScale` call.
3532
+
3533
+ Parameters
3534
+ ----------
3535
+ s : array_like, float or int
3536
+ Scaling factor(s). If `s` is scalar (float), scaling will be uniform.
3537
+ Providing a vector of scaling values [sx, sy, sz] will result in an
3538
+ anisotropic scaling matrix if any of the values differ.
3539
+ out : ndarray, optional
3540
+ Optional output array. Must be same `shape` and `dtype` as the expected
3541
+ output if `out` was not specified.
3542
+ dtype : dtype or str, optional
3543
+ Data type for computations can either be 'float32' or 'float64'. If
3544
+ `out` is specified, the data type of `out` is used and this argument is
3545
+ ignored. If `out` is not provided, 'float64' is used by default.
3546
+
3547
+ Returns
3548
+ -------
3549
+ ndarray
3550
+ 4x4 scaling matrix in row-major order.
3551
+
3552
+ """
3553
+ # from glScale
3554
+ if out is None:
3555
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3556
+ S = np.zeros((4, 4,), dtype=dtype)
3557
+ else:
3558
+ dtype = np.dtype(out.dtype).type
3559
+ S = out
3560
+ S.fill(0.0)
3561
+
3562
+ if isinstance(s, (float, int,)):
3563
+ S[0, 0] = S[1, 1] = S[2, 2] = dtype(s)
3564
+ else:
3565
+ S[0, 0] = dtype(s[0])
3566
+ S[1, 1] = dtype(s[1])
3567
+ S[2, 2] = dtype(s[2])
3568
+
3569
+ S[3, 3] = 1.0
3570
+
3571
+ return S
3572
+
3573
+
3574
+ def rotationMatrix(angle, axis=(0., 0., -1.), out=None, dtype=None):
3575
+ """Create a rotation matrix.
3576
+
3577
+ The resulting matrix will rotate points about `axis` by `angle`. The
3578
+ resulting matrix is similar to that produced by a `glRotate` call.
3579
+
3580
+ Parameters
3581
+ ----------
3582
+ angle : float
3583
+ Rotation angle in degrees.
3584
+ axis : array_like or str
3585
+ Axis vector components or axis name. If a vector, input must be length
3586
+ 3. A string can be specified for rotations about world axes (eg. `'+x'`,
3587
+ `'-z'`, `'+y'`, etc.)
3588
+ out : ndarray, optional
3589
+ Optional output array. Must be same `shape` and `dtype` as the expected
3590
+ output if `out` was not specified.
3591
+ dtype : dtype or str, optional
3592
+ Data type for computations can either be 'float32' or 'float64'. If
3593
+ `out` is specified, the data type of `out` is used and this argument is
3594
+ ignored. If `out` is not provided, 'float64' is used by default.
3595
+
3596
+ Returns
3597
+ -------
3598
+ ndarray
3599
+ 4x4 scaling matrix in row-major order. Will be the same array as `out`
3600
+ if specified, if not, a new array will be allocated.
3601
+
3602
+ Notes
3603
+ -----
3604
+ * Vector `axis` is normalized before creating the matrix.
3605
+
3606
+ """
3607
+ if out is None:
3608
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3609
+ R = np.zeros((4, 4,), dtype=dtype)
3610
+ else:
3611
+ dtype = np.dtype(out.dtype).type
3612
+ R = out
3613
+ R.fill(0.0)
3614
+
3615
+ try:
3616
+ axis = VEC_AXES[axis] if isinstance(axis, str) else axis
3617
+ except KeyError:
3618
+ raise ValueError(
3619
+ "Value of `axis` must be either '+x', '-x', '+y', '-x', '+z' or "
3620
+ "'-z' or length 3 vector.")
3621
+
3622
+ axis = normalize(axis, dtype=dtype)
3623
+ if np.count_nonzero(axis) == 0:
3624
+ raise ValueError("Value for `axis` is zero-length.")
3625
+
3626
+ angle = np.radians(angle, dtype=dtype)
3627
+ c = np.cos(angle, dtype=dtype)
3628
+ s = np.sin(angle, dtype=dtype)
3629
+
3630
+ xs, ys, zs = axis * s
3631
+ x2, y2, z2 = np.square(axis) # type inferred by input
3632
+ x, y, z = axis
3633
+ cd = dtype(1.0) - c
3634
+
3635
+ R[0, 0] = x2 * cd + c
3636
+ R[0, 1] = x * y * cd - zs
3637
+ R[0, 2] = x * z * cd + ys
3638
+
3639
+ R[1, 0] = y * x * cd + zs
3640
+ R[1, 1] = y2 * cd + c
3641
+ R[1, 2] = y * z * cd - xs
3642
+
3643
+ R[2, 0] = x * z * cd - ys
3644
+ R[2, 1] = y * z * cd + xs
3645
+ R[2, 2] = z2 * cd + c
3646
+
3647
+ R[3, 3] = dtype(1.0)
3648
+ R[:, :] += 0.0 # remove negative zeros
3649
+
3650
+ return R
3651
+
3652
+
3653
+ def translationMatrix(t, out=None, dtype=None):
3654
+ """Create a translation matrix.
3655
+
3656
+ The resulting matrix is the same as generated by a `glTranslate` call.
3657
+
3658
+ Parameters
3659
+ ----------
3660
+ t : ndarray, tuple, or list of float
3661
+ Translation vector [tx, ty, tz].
3662
+ out : ndarray, optional
3663
+ Optional output array. Must be same `shape` and `dtype` as the expected
3664
+ output if `out` was not specified.
3665
+ dtype : dtype or str, optional
3666
+ Data type for computations can either be 'float32' or 'float64'. If
3667
+ `out` is specified, the data type of `out` is used and this argument is
3668
+ ignored. If `out` is not provided, 'float64' is used by default.
3669
+
3670
+ Returns
3671
+ -------
3672
+ ndarray
3673
+ 4x4 translation matrix in row-major order. Will be the same array as
3674
+ `out` if specified, if not, a new array will be allocated.
3675
+
3676
+ """
3677
+ if out is None:
3678
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3679
+ T = np.identity(4, dtype=dtype)
3680
+ else:
3681
+ dtype = np.dtype(out.dtype).type
3682
+ T = out
3683
+ T.fill(0.0)
3684
+ np.fill_diagonal(T, 1.0)
3685
+
3686
+ T[:3, 3] = np.asarray(t, dtype=dtype)
3687
+
3688
+ return T
3689
+
3690
+
3691
+ def invertMatrix(m, out=None, dtype=None):
3692
+ """Invert a square matrix.
3693
+
3694
+ Parameters
3695
+ ----------
3696
+ m : array_like
3697
+ Square matrix to invert. Inputs can be 4x4, 3x3 or 2x2.
3698
+ out : ndarray, optional
3699
+ Optional output array. Must be same `shape` and `dtype` as the expected
3700
+ output if `out` was not specified.
3701
+ dtype : dtype or str, optional
3702
+ Data type for computations can either be 'float32' or 'float64'. If
3703
+ `out` is specified, the data type of `out` is used and this argument is
3704
+ ignored. If `out` is not provided, 'float64' is used by default.
3705
+
3706
+ Returns
3707
+ -------
3708
+ ndarray
3709
+ Matrix which is the inverse of `m`
3710
+
3711
+ """
3712
+ if out is None:
3713
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3714
+ else:
3715
+ dtype = out.dtype
3716
+
3717
+ m = np.asarray(m, dtype=dtype) # input as array
3718
+ toReturn = np.empty_like(m, dtype=dtype) if out is None else out
3719
+ toReturn.fill(0.0)
3720
+
3721
+ if m.shape == (4, 4,):
3722
+ # Special handling of 4x4 matrices, if affine and orthogonal
3723
+ # (homogeneous), simply transpose the matrix rather than doing a full
3724
+ # invert.
3725
+ if isOrthogonal(m[:3, :3]) and isAffine(m):
3726
+ rg = m[:3, :3]
3727
+ toReturn[:3, :3] = rg.T
3728
+ toReturn[:3, 3] = -m[:3, 3].dot(rg)
3729
+ #toReturn[0, 3] = \
3730
+ # -(m[0, 0] * m[0, 3] + m[1, 0] * m[1, 3] + m[2, 0] * m[2, 3])
3731
+ #toReturn[1, 3] = \
3732
+ # -(m[0, 1] * m[0, 3] + m[1, 1] * m[1, 3] + m[2, 1] * m[2, 3])
3733
+ #toReturn[2, 3] = \
3734
+ # -(m[0, 2] * m[0, 3] + m[1, 2] * m[1, 3] + m[2, 2] * m[2, 3])
3735
+ toReturn[3, 3] = 1.0
3736
+ else:
3737
+ toReturn[:, :] = np.linalg.inv(m)
3738
+ elif m.shape[0] == m.shape[1]: # square, other than 4x4
3739
+ toReturn[:, :] = np.linalg.inv(m) if not isOrthogonal(m) else m.T
3740
+ else:
3741
+ toReturn[:, :] = np.linalg.inv(m)
3742
+
3743
+ return toReturn
3744
+
3745
+
3746
+ def multMatrix(matrices, reverse=False, out=None, dtype=None):
3747
+ """Chain multiplication of two or more matrices.
3748
+
3749
+ Multiply a sequence of matrices together, reducing to a single product
3750
+ matrix. For instance, specifying `matrices` the sequence of matrices (A, B,
3751
+ C, D) will return the product (((AB)C)D). If `reverse=True`, the product
3752
+ will be (A(B(CD))).
3753
+
3754
+ Alternatively, a 3D array can be specified to `matrices` as a stack, where
3755
+ an index along axis 0 references a 2D slice storing matrix values. The
3756
+ product of the matrices along the axis will be returned. This is a bit more
3757
+ efficient than specifying separate matrices in a sequence, but the
3758
+ difference is negligible when only a few matrices are being multiplied.
3759
+
3760
+ Parameters
3761
+ ----------
3762
+ matrices : list, tuple or ndarray
3763
+ Sequence or stack of matrices to multiply. All matrices must have the
3764
+ same dimensions.
3765
+ reverse : bool, optional
3766
+ Multiply matrices right-to-left. This is useful when dealing with
3767
+ transformation matrices, where the order of operations for transforms
3768
+ will appear the same as the order the matrices are specified. Default is
3769
+ 'False'. When `True`, this function behaves similarly to
3770
+ :func:`concatenate`.
3771
+ out : ndarray, optional
3772
+ Optional output array. Must be same `shape` and `dtype` as the expected
3773
+ output if `out` was not specified.
3774
+ dtype : dtype or str, optional
3775
+ Data type for computations can either be 'float32' or 'float64'. If
3776
+ `out` is specified, the data type of `out` is used and this argument is
3777
+ ignored. If `out` is not provided, 'float64' is used by default.
3778
+
3779
+ Returns
3780
+ -------
3781
+ ndarray
3782
+ Matrix product.
3783
+
3784
+ Notes
3785
+ -----
3786
+ * You may use `numpy.matmul` when dealing with only two matrices instead of
3787
+ `multMatrix`.
3788
+ * If a single matrix is specified, the returned product will have the same
3789
+ values.
3790
+
3791
+ Examples
3792
+ --------
3793
+ Chain multiplication of SRT matrices::
3794
+
3795
+ translate = translationMatrix((0.035, 0, -0.5))
3796
+ rotate = rotationMatrix(90.0, (0, 1, 0))
3797
+ scale = scaleMatrix(2.0)
3798
+
3799
+ SRT = multMatrix((translate, rotate, scale))
3800
+
3801
+ Same as above, but matrices are in a 3x4x4 array::
3802
+
3803
+ matStack = np.array((translate, rotate, scale))
3804
+
3805
+ # or ...
3806
+ # matStack = np.zeros((3, 4, 4))
3807
+ # matStack[0, :, :] = translate
3808
+ # matStack[1, :, :] = rotate
3809
+ # matStack[2, :, :] = scale
3810
+
3811
+ SRT = multMatrix(matStack)
3812
+
3813
+ Using `reverse=True` allows you to specify transformation matrices in the
3814
+ order which they will be applied::
3815
+
3816
+ SRT = multMatrix(np.array((scale, rotate, translate)), reverse=True)
3817
+
3818
+ """
3819
+ # convert matrix types
3820
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3821
+ matrices = np.asarray(matrices, dtype=dtype) # convert to array
3822
+
3823
+ matrices = np.atleast_3d(matrices)
3824
+ prod = functools.reduce(
3825
+ np.matmul, matrices[:] if not reverse else matrices[::-1])
3826
+
3827
+ if out is not None:
3828
+ toReturn = out
3829
+ toReturn[:, :] = prod
3830
+ else:
3831
+ toReturn = prod
3832
+
3833
+ return toReturn
3834
+
3835
+
3836
+ def concatenate(matrices, out=None, dtype=None):
3837
+ """Concatenate matrix transformations.
3838
+
3839
+ Chain multiply matrices describing transform operations into a single matrix
3840
+ product, that when applied, transforms points and vectors with each
3841
+ operation in the order they're specified.
3842
+
3843
+ Parameters
3844
+ ----------
3845
+ matrices : list or tuple
3846
+ List of matrices to concatenate. All matrices must all have the same
3847
+ size, usually 4x4 or 3x3.
3848
+ out : ndarray, optional
3849
+ Optional output array. Must be same `shape` and `dtype` as the expected
3850
+ output if `out` was not specified.
3851
+ dtype : dtype or str, optional
3852
+ Data type for computations can either be 'float32' or 'float64'. If
3853
+ `out` is specified, the data type of `out` is used and this argument is
3854
+ ignored. If `out` is not provided, 'float64' is used by default.
3855
+
3856
+ Returns
3857
+ -------
3858
+ ndarray
3859
+ Matrix product.
3860
+
3861
+ See Also
3862
+ --------
3863
+ * multMatrix : Chain multiplication of matrices.
3864
+
3865
+ Notes
3866
+ -----
3867
+ * This function should only be used for combining transformation matrices.
3868
+ Use `multMatrix` for general matrix chain multiplication.
3869
+
3870
+ Examples
3871
+ --------
3872
+ Create an SRT (scale, rotate, and translate) matrix to convert model-space
3873
+ coordinates to world-space::
3874
+
3875
+ S = scaleMatrix([2.0, 2.0, 2.0]) # scale model 2x
3876
+ R = rotationMatrix(-90., [0., 0., -1]) # rotate -90 about -Z axis
3877
+ T = translationMatrix([0., 0., -5.]) # translate point 5 units away
3878
+
3879
+ # product matrix when applied to points will scale, rotate and transform
3880
+ # in that order.
3881
+ SRT = concatenate([S, R, T])
3882
+
3883
+ # transform a point in model-space coordinates to world-space
3884
+ pointModel = np.array([0., 1., 0., 1.])
3885
+ pointWorld = np.matmul(SRT, pointModel.T) # point in WCS
3886
+ # ... or ...
3887
+ pointWorld = matrixApply(SRT, pointModel)
3888
+
3889
+ Create a model-view matrix from a world-space pose represented by an
3890
+ orientation (quaternion) and position (vector). The resulting matrix will
3891
+ transform model-space coordinates to eye-space::
3892
+
3893
+ # eye pose as quaternion and vector
3894
+ stimOri = quatFromAxisAngle([0., 0., -1.], -45.0)
3895
+ stimPos = [0., 1.5, -5.]
3896
+
3897
+ # create model matrix
3898
+ R = quatToMatrix(stimOri)
3899
+ T = translationMatrix(stimPos)
3900
+ M = concatenate(R, T) # model matrix
3901
+
3902
+ # create a view matrix, can also be represented as 'pos' and 'ori'
3903
+ eyePos = [0., 1.5, 0.]
3904
+ eyeFwd = [0., 0., -1.]
3905
+ eyeUp = [0., 1., 0.]
3906
+ V = lookAt(eyePos, eyeFwd, eyeUp) # from viewtools
3907
+
3908
+ # modelview matrix
3909
+ MV = concatenate([M, V])
3910
+
3911
+ You can put the created matrix in the OpenGL matrix stack as shown below.
3912
+ Note that the matrix must have a 32-bit floating-point data type and needs
3913
+ to be loaded transposed since OpenGL takes matrices in column-major order::
3914
+
3915
+ GL.glMatrixMode(GL.GL_MODELVIEW)
3916
+
3917
+ # pyglet
3918
+ MV = np.asarray(MV, dtype='float32') # must be 32-bit float!
3919
+ ptrMV = MV.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
3920
+ GL.glLoadTransposeMatrixf(ptrMV)
3921
+
3922
+ # PyOpenGL
3923
+ MV = np.asarray(MV, dtype='float32')
3924
+ GL.glLoadTransposeMatrixf(MV)
3925
+
3926
+ Furthermore, you can convert a point from model-space to homogeneous
3927
+ clip-space by concatenating the projection, view, and model matrices::
3928
+
3929
+ # compute projection matrix, functions here are from 'viewtools'
3930
+ screenWidth = 0.52
3931
+ screenAspect = w / h
3932
+ scrDistance = 0.55
3933
+ frustum = computeFrustum(screenWidth, screenAspect, scrDistance)
3934
+ P = perspectiveProjectionMatrix(*frustum)
3935
+
3936
+ # multiply model-space points by MVP to convert them to clip-space
3937
+ MVP = concatenate([M, V, P])
3938
+ pointModel = np.array([0., 1., 0., 1.])
3939
+ pointClipSpace = np.matmul(MVP, pointModel.T)
3940
+
3941
+ """
3942
+ return multMatrix(matrices, reverse=True, out=out, dtype=dtype)
3943
+
3944
+
3945
+ def matrixFromEulerAngles(rx, ry, rz, degrees=True, out=None, dtype=None):
3946
+ """Construct a 4x4 rotation matrix from Euler angles.
3947
+
3948
+ Rotations are combined by first rotating about the X axis, then Y, and
3949
+ finally Z.
3950
+
3951
+ Parameters
3952
+ ----------
3953
+ rx, ry, rz : float
3954
+ Rotation angles (pitch, yaw, and roll).
3955
+ degrees : bool, optional
3956
+ Rotation angles are specified in degrees. If `False`, they will be
3957
+ assumed as radians. Default is `True`.
3958
+ out : ndarray, optional
3959
+ Optional output array. Must be same `shape` and `dtype` as the expected
3960
+ output if `out` was not specified.
3961
+ dtype : dtype or str, optional
3962
+ Data type for computations can either be 'float32' or 'float64'. If
3963
+ `out` is specified, the data type of `out` is used and this argument is
3964
+ ignored. If `out` is not provided, 'float64' is used by default.
3965
+
3966
+ Returns
3967
+ -------
3968
+ ndarray
3969
+ 4x4 rotation matrix.
3970
+
3971
+ Examples
3972
+ --------
3973
+ Demonstration of how a combination of axis-angle rotations is equivalent
3974
+ to a single call of `matrixFromEulerAngles`::
3975
+
3976
+ m1 = matrixFromEulerAngles(90., 45., 135.))
3977
+
3978
+ # construct rotation matrix from 3 orthogonal rotations
3979
+ rx = rotationMatrix(90., (1, 0, 0)) # x-axis
3980
+ ry = rotationMatrix(45., (0, 1, 0)) # y-axis
3981
+ rz = rotationMatrix(135., (0, 0, 1)) # z-axis
3982
+ m2 = concatenate([rz, ry, rx]) # note the order
3983
+
3984
+ print(numpy.allclose(m1, m2)) # True
3985
+
3986
+ Not only does `matrixFromEulerAngles` require less code, it also is
3987
+ considerably more efficient than constructing and multiplying multiple
3988
+ matrices.
3989
+
3990
+ """
3991
+ # from https://www.j3d.org/matrix_faq/matrfaq_latest.html
3992
+ if out is None:
3993
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3994
+ toReturn = np.zeros((4, 4,), dtype=dtype)
3995
+ else:
3996
+ dtype = np.dtype(dtype).type
3997
+ toReturn = out
3998
+ toReturn.fill(0.0)
3999
+
4000
+ angles = np.asarray([rx, ry, rz], dtype=dtype)
4001
+ if degrees:
4002
+ angles = np.radians(angles)
4003
+
4004
+ a, c, e = np.cos(angles)
4005
+ b, d, f = np.sin(angles)
4006
+ ad = a * d
4007
+ bd = b * d
4008
+
4009
+ toReturn[0, 0] = c * e
4010
+ toReturn[0, 1] = -c * f
4011
+ toReturn[0, 2] = d
4012
+ toReturn[1, 0] = bd * e + a * f
4013
+ toReturn[1, 1] = -bd * f + a * e
4014
+ toReturn[1, 2] = -b * c
4015
+ toReturn[2, 0] = -ad * e + b * f
4016
+ toReturn[2, 1] = ad * f + b * e
4017
+ toReturn[2, 2] = a * c
4018
+ toReturn[3, 3] = 1.0
4019
+
4020
+ return toReturn
4021
+
4022
+
4023
+ def isOrthogonal(m):
4024
+ """Check if a square matrix is orthogonal.
4025
+
4026
+ If a matrix is orthogonal, its columns form an orthonormal basis and is
4027
+ non-singular. An orthogonal matrix is invertible by simply taking the
4028
+ transpose of the matrix.
4029
+
4030
+ Parameters
4031
+ ----------
4032
+ m : array_like
4033
+ Square matrix, either 2x2, 3x3 or 4x4.
4034
+
4035
+ Returns
4036
+ -------
4037
+ bool
4038
+ `True` if the matrix is orthogonal.
4039
+
4040
+ """
4041
+ if not isinstance(m, (np.ndarray,)):
4042
+ m = np.asarray(m)
4043
+
4044
+ assert 2 <= m.shape[0] <= 4 # 2x2 to 4x4
4045
+ assert m.shape[0] == m.shape[1] # must be square
4046
+
4047
+ dtype = np.dtype(m.dtype).type
4048
+ return np.allclose(np.matmul(m.T, m, dtype=dtype),
4049
+ np.identity(m.shape[0], dtype))
4050
+
4051
+
4052
+ def isAffine(m):
4053
+ """Check if a 4x4 square matrix describes an affine transformation.
4054
+
4055
+ Parameters
4056
+ ----------
4057
+ m : array_like
4058
+ 4x4 transformation matrix.
4059
+
4060
+ Returns
4061
+ -------
4062
+ bool
4063
+ `True` if the matrix is affine.
4064
+
4065
+ """
4066
+ assert m.shape[0] == m.shape[1] == 4
4067
+
4068
+ if not isinstance(m, (np.ndarray,)):
4069
+ m = np.asarray(m)
4070
+
4071
+ dtype = np.dtype(m.dtype).type
4072
+ eps = np.finfo(dtype).eps
4073
+
4074
+ return np.all(m[3, :3] < eps) and (dtype(1.0) - m[3, 3]) < eps
4075
+
4076
+
4077
+ def applyMatrix(m, points, out=None, dtype=None):
4078
+ """Apply a matrix over a 2D array of points.
4079
+
4080
+ This function behaves similarly to the following `Numpy` statement::
4081
+
4082
+ points[:, :] = points.dot(m.T)
4083
+
4084
+ Transformation matrices specified to `m` must have dimensions 4x4, 3x4, 3x3
4085
+ or 2x2. With the exception of 4x4 matrices, input `points` must have the
4086
+ same number of columns as the matrix has rows. 4x4 matrices can be used to
4087
+ transform both Nx4 and Nx3 arrays.
4088
+
4089
+ Parameters
4090
+ ----------
4091
+ m : array_like
4092
+ Matrix with dimensions 2x2, 3x3, 3x4 or 4x4.
4093
+ points : array_like
4094
+ 2D array of points/coordinates to transform. Each row should have length
4095
+ appropriate for the matrix being used. If not, a square submatrix will
4096
+ be taken from the input matrix with dimensions equal to the number of
4097
+ columns in `points`.
4098
+ out : ndarray, optional
4099
+ Optional output array. Must be same `shape` and `dtype` as the expected
4100
+ output if `out` was not specified.
4101
+ dtype : dtype or str, optional
4102
+ Data type for computations can either be 'float32' or 'float64'. If
4103
+ `out` is specified, the data type of `out` is used and this argument is
4104
+ ignored. If `out` is not provided, 'float64' is used by default.
4105
+
4106
+ Returns
4107
+ -------
4108
+ ndarray
4109
+ Transformed coordinates.
4110
+
4111
+ Notes
4112
+ -----
4113
+ * Input (`points`) and output (`out`) arrays cannot be the same instance for
4114
+ this function.
4115
+ * In the case of 4x4 input matrices, this function performs optimizations
4116
+ based on whether the input matrix is affine, greatly improving performance
4117
+ when working with Nx3 arrays.
4118
+
4119
+ Examples
4120
+ --------
4121
+ Construct a matrix and transform a point::
4122
+
4123
+ # identity 3x3 matrix for this example
4124
+ M = [[1.0, 0.0, 0.0],
4125
+ [0.0, 1.0, 0.0],
4126
+ [0.0, 0.0, 1.0]]
4127
+
4128
+ pnt = [1.0, 0.0, 0.0]
4129
+
4130
+ pntNew = applyMatrix(M, pnt)
4131
+
4132
+ Construct an SRT matrix (scale, rotate, transform) and transform an array of
4133
+ points::
4134
+
4135
+ S = scaleMatrix([5.0, 5.0, 5.0]) # scale 5x
4136
+ R = rotationMatrix(180., [0., 0., -1]) # rotate 180 degrees
4137
+ T = translationMatrix([0., 1.5, -3.]) # translate point up and away
4138
+ M = concatenate([S, R, T]) # create transform matrix
4139
+
4140
+ # points to transform
4141
+ points = np.array([[0., 1., 0., 1.], [-1., 0., 0., 1.]]) # [x, y, z, w]
4142
+ newPoints = applyMatrix(M, points) # apply the transformation
4143
+
4144
+ Convert CIE-XYZ colors to sRGB::
4145
+
4146
+ sRGBMatrix = [[3.2404542, -1.5371385, -0.4985314],
4147
+ [-0.969266, 1.8760108, 0.041556 ],
4148
+ [0.0556434, -0.2040259, 1.0572252]]
4149
+
4150
+ colorsRGB = applyMatrix(sRGBMatrix, colorsXYZ)
4151
+
4152
+ """
4153
+ if out is None:
4154
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4155
+ else:
4156
+ dtype = np.dtype(out.dtype).type
4157
+
4158
+ m = np.asarray(m, dtype=dtype)
4159
+ points = np.asarray(points, dtype=dtype)
4160
+
4161
+ if out is None:
4162
+ toReturn = np.zeros_like(points, dtype=dtype)
4163
+ else:
4164
+ if id(out) == id(points):
4165
+ raise ValueError('Output array cannot be same as input.')
4166
+ toReturn = out
4167
+
4168
+ pout, p = np.atleast_2d(toReturn, points)
4169
+
4170
+ nCols = p.shape[1]
4171
+ if m.shape[0] > nCols:
4172
+ if m.shape[1] < nCols:
4173
+ raise ValueError(
4174
+ 'Input matrix dimensions are not compatible with input array.')
4175
+ m = m[:nCols, :nCols] # take sub matrix
4176
+
4177
+ if m.shape == (4, 4): # 4x4 matrix
4178
+ if pout.shape[1] == 3: # Nx3
4179
+ pout[:, :] = p.dot(m[:3, :3].T)
4180
+ pout += m[:3, 3]
4181
+ # find `rcpW` as suggested in OpenXR's xr_linear.h header
4182
+ # reciprocal of `w` if the matrix is not orthonormal
4183
+ if not isAffine(m):
4184
+ rcpW = 1.0 / (m[3, 0] * p[:, 0] +
4185
+ m[3, 1] * p[:, 1] +
4186
+ m[3, 2] * p[:, 2] +
4187
+ m[3, 3])
4188
+ pout *= rcpW[:, np.newaxis]
4189
+ elif pout.shape[1] == 4: # Nx4
4190
+ pout[:, :] = p.dot(m.T)
4191
+ else:
4192
+ raise ValueError(
4193
+ 'Input array dimensions invalid. Should be Nx3 or Nx4 when '
4194
+ 'input matrix is 4x4.')
4195
+ elif m.shape == (3, 4): # 3x4 matrix
4196
+ if pout.shape[1] == 3: # Nx3
4197
+ pout[:, :] = p.dot(m[:3, :3].T)
4198
+ pout += m[:3, 3]
4199
+ else:
4200
+ raise ValueError(
4201
+ 'Input array dimensions invalid. Should be Nx3 when input '
4202
+ 'matrix is 3x4.')
4203
+ elif m.shape == (3, 3): # 3x3 matrix, e.g colors
4204
+ if pout.shape[1] == 3: # Nx3
4205
+ pout[:, :] = p.dot(m.T)
4206
+ else:
4207
+ raise ValueError(
4208
+ 'Input array dimensions invalid. Should be Nx3 when '
4209
+ 'input matrix is 3x3.')
4210
+ elif m.shape == (2, 2): # 2x2 matrix
4211
+ if pout.shape[1] == 2: # Nx2
4212
+ pout[:, :] = p.dot(m.T)
4213
+ else:
4214
+ raise ValueError(
4215
+ 'Input array dimensions invalid. Should be Nx2 when '
4216
+ 'input matrix is 2x2.')
4217
+ else:
4218
+ raise ValueError(
4219
+ 'Only a square matrix with dimensions 2, 3 or 4 can be used.')
4220
+
4221
+ return toReturn
4222
+
4223
+
4224
+ def posOriToMatrix(pos, ori, out=None, dtype=None):
4225
+ """Convert a rigid body pose to a 4x4 transformation matrix.
4226
+
4227
+ A pose is represented by a position coordinate `pos` and orientation
4228
+ quaternion `ori`.
4229
+
4230
+ Parameters
4231
+ ----------
4232
+ pos : ndarray, tuple, or list of float
4233
+ Position vector [x, y, z].
4234
+ ori : tuple, list or ndarray of float
4235
+ Orientation quaternion in form [x, y, z, w] where w is real and x, y, z
4236
+ are imaginary components.
4237
+ out : ndarray, optional
4238
+ Optional output array. Must be same `shape` and `dtype` as the expected
4239
+ output if `out` was not specified.
4240
+ dtype : dtype or str, optional
4241
+ Data type for computations can either be 'float32' or 'float64'. If
4242
+ `out` is specified, the data type of `out` is used and this argument is
4243
+ ignored. If `out` is not provided, 'float64' is used by default.
4244
+
4245
+ Returns
4246
+ -------
4247
+ ndarray
4248
+ 4x4 transformation matrix.
4249
+
4250
+ """
4251
+ if out is None:
4252
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4253
+ toReturn = np.zeros((4, 4,), dtype=dtype)
4254
+ else:
4255
+ dtype = np.dtype(dtype).type
4256
+ toReturn = out
4257
+
4258
+ transMat = translationMatrix(pos, dtype=dtype)
4259
+ rotMat = quatToMatrix(ori, dtype=dtype)
4260
+
4261
+ return np.matmul(transMat, rotMat, out=toReturn)
4262
+
4263
+
4264
+ def transform(pos, ori, points, out=None, dtype=None):
4265
+ """Transform points using a position and orientation. Points are rotated
4266
+ then translated.
4267
+
4268
+ Parameters
4269
+ ----------
4270
+ pos : array_like
4271
+ Position vector in form [x, y, z] or [x, y, z, 1].
4272
+ ori : array_like
4273
+ Orientation quaternion in form [x, y, z, w] where w is real and x, y, z
4274
+ are imaginary components.
4275
+ points : array_like
4276
+ Point(s) [x, y, z] to transform.
4277
+ out : ndarray, optional
4278
+ Optional output array. Must be same `shape` and `dtype` as the expected
4279
+ output if `out` was not specified.
4280
+ dtype : dtype or str, optional
4281
+ Data type for computations can either be 'float32' or 'float64'. If
4282
+ `out` is specified, the data type of `out` is used and this argument is
4283
+ ignored. If `out` is not provided, 'float64' is used by default.
4284
+
4285
+ Returns
4286
+ -------
4287
+ ndarray
4288
+ Transformed points.
4289
+
4290
+ Examples
4291
+ --------
4292
+ Transform points by a position coordinate and orientation quaternion::
4293
+
4294
+ # rigid body pose
4295
+ ori = quatFromAxisAngle([0., 0., -1.], 90.0, degrees=True)
4296
+ pos = [0., 1.5, -3.]
4297
+ # points to transform
4298
+ points = np.array([[0., 1., 0., 1.], [-1., 0., 0., 1.]]) # [x, y, z, 1]
4299
+ outPoints = np.zeros_like(points) # output array
4300
+ transform(pos, ori, points, out=outPoints) # do the transformation
4301
+
4302
+ You can get the same results as the previous example using a matrix by doing
4303
+ the following::
4304
+
4305
+ R = rotationMatrix(90., [0., 0., -1])
4306
+ T = translationMatrix([0., 1.5, -3.])
4307
+ M = concatenate([R, T])
4308
+ applyMatrix(M, points, out=outPoints)
4309
+
4310
+ If you are defining transformations with quaternions and coordinates, you
4311
+ can skip the costly matrix creation process by using `transform`.
4312
+
4313
+ Notes
4314
+ -----
4315
+ * In performance tests, `applyMatrix` is noticeably faster than `transform`
4316
+ for very large arrays, however this is only true if you are applying the
4317
+ same transformation to all points.
4318
+ * If the input arrays for `points` or `pos` is Nx4, the last column is
4319
+ ignored.
4320
+
4321
+ """
4322
+ if out is None:
4323
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4324
+ else:
4325
+ dtype = np.dtype(dtype).type
4326
+
4327
+ pos = np.asarray(pos, dtype=dtype)
4328
+ ori = np.asarray(ori, dtype=dtype)
4329
+ points = np.asarray(points, dtype=dtype)
4330
+
4331
+ if out is None:
4332
+ toReturn = np.zeros_like(points, dtype=dtype)
4333
+ else:
4334
+ if out.shape != points.shape:
4335
+ raise ValueError(
4336
+ "Array 'out' and 'points' do not have matching shapes.")
4337
+
4338
+ toReturn = out
4339
+
4340
+ pout, points, pos2d = np.atleast_2d(toReturn, points, pos) # create 2d views
4341
+
4342
+ # apply rotation
4343
+ applyQuat(ori, points, out=pout)
4344
+
4345
+ # apply translation
4346
+ pout[:, 0] += pos2d[:, 0]
4347
+ pout[:, 1] += pos2d[:, 1]
4348
+ pout[:, 2] += pos2d[:, 2]
4349
+
4350
+ return toReturn
4351
+
4352
+
4353
+ def scale(sf, points, out=None, dtype=None):
4354
+ """Scale points by a factor.
4355
+
4356
+ This is useful for converting points between units, and to stretch or
4357
+ compress points along a given axis. Scaling can be uniform which the same
4358
+ factor is applied along all axes, or anisotropic along specific axes.
4359
+
4360
+ Parameters
4361
+ ----------
4362
+ sf : array_like or float
4363
+ Scaling factor. If scalar, all points will be scaled uniformly by that
4364
+ factor. If a vector, scaling will be anisotropic along an axis.
4365
+ points : array_like
4366
+ Point(s) [x, y, z] to scale.
4367
+ out : ndarray, optional
4368
+ Optional output array. Must be same `shape` and `dtype` as the expected
4369
+ output if `out` was not specified.
4370
+ dtype : dtype or str, optional
4371
+ Data type for computations can either be 'float32' or 'float64'. If
4372
+ `out` is specified, the data type of `out` is used and this argument is
4373
+ ignored. If `out` is not provided, 'float64' is used by default.
4374
+
4375
+ Returns
4376
+ -------
4377
+ ndarray
4378
+ Scaled points.
4379
+
4380
+ Examples
4381
+ --------
4382
+ Apply uniform scaling to points, here we scale to convert points in
4383
+ centimeters to meters::
4384
+
4385
+ CM_TO_METERS = 1.0 / 100.0
4386
+ pointsCM = [[1, 2, 3], [4, 5, 6], [-1, 1, 0]]
4387
+ pointsM = scale(CM_TO_METERS, pointsCM)
4388
+
4389
+ Anisotropic scaling along the X and Y axis::
4390
+
4391
+ pointsM = scale((SCALE_FACTOR_X, SCALE_FACTOR_Y), pointsCM)
4392
+
4393
+ Scale only on the X axis::
4394
+
4395
+ pointsM = scale((SCALE_FACTOR_X,), pointsCM)
4396
+
4397
+ Apply scaling on the Z axis only::
4398
+
4399
+ pointsM = scale((1.0, 1.0, SCALE_FACTOR_Z), pointsCM)
4400
+
4401
+ """
4402
+ if out is None:
4403
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4404
+ else:
4405
+ dtype = np.dtype(dtype).type
4406
+
4407
+ points = np.asarray(points, dtype=dtype)
4408
+ toReturn = np.zeros_like(points, dtype=dtype) if out is None else out
4409
+ toReturn, points = np.atleast_2d(toReturn, points) # create 2d views
4410
+
4411
+ # uniform scaling
4412
+ if isinstance(sf, (float, int)):
4413
+ toReturn[:, :] = points * sf
4414
+ elif isinstance(sf, (list, tuple, np.ndarray)): # anisotropic
4415
+ sf = np.asarray(sf, dtype=dtype)
4416
+ sfLen = len(sf)
4417
+ if sfLen <= 3:
4418
+ toReturn[:, :] = points
4419
+ toReturn[:, :len(sf)] *= sf
4420
+ else:
4421
+ raise ValueError("Scale factor array must have length <= 3.")
4422
+
4423
+ return toReturn
4424
+
4425
+
4426
+ def normalMatrix(modelMatrix, out=None, dtype=None):
4427
+ """Get the normal matrix from a model matrix.
4428
+
4429
+ Parameters
4430
+ ----------
4431
+ modelMatrix : array_like
4432
+ 4x4 homogeneous model matrix.
4433
+ out : ndarray, optional
4434
+ Optional output array. Must be same `shape` and `dtype` as the expected
4435
+ output if `out` was not specified.
4436
+ dtype : dtype or str, optional
4437
+ Data type for computations can either be 'float32' or 'float64'. If
4438
+ `out` is specified, the data type of `out` is used and this argument is
4439
+ ignored. If `out` is not provided, 'float64' is used by default.
4440
+
4441
+ Returns
4442
+ -------
4443
+ ndarray
4444
+ Normal matrix.
4445
+
4446
+ """
4447
+ if out is None:
4448
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4449
+ else:
4450
+ dtype = np.dtype(dtype).type
4451
+
4452
+ modelMatrix = np.asarray(modelMatrix, dtype=dtype)
4453
+
4454
+ toReturn = np.zeros((4, 4), dtype=dtype) if out is None else out
4455
+ toReturn[:, :] = np.linalg.inv(modelMatrix).T
4456
+
4457
+ return toReturn
4458
+
4459
+
4460
+ def forwardProject(objPos, modelView, proj, viewport=None, out=None, dtype=None):
4461
+ """Project a point in a scene to a window coordinate.
4462
+
4463
+ This function is similar to `gluProject` and can be used to find the window
4464
+ coordinate which a point projects to.
4465
+
4466
+ Parameters
4467
+ ----------
4468
+ objPos : array_like
4469
+ Object coordinates (x, y, z). If an Nx3 array of coordinates is
4470
+ specified, where each row contains a window coordinate this function
4471
+ will return an array of projected coordinates with the same size.
4472
+ modelView : array_like
4473
+ 4x4 combined model and view matrix for returned value to be object
4474
+ coordinates. Specify only the view matrix for a coordinate in the scene.
4475
+ proj : array_like
4476
+ 4x4 projection matrix used for rendering.
4477
+ viewport : array_like
4478
+ Viewport rectangle for the window [x, y, w, h]. If not specified, the
4479
+ returned values will be in normalized device coordinates.
4480
+ out : ndarray, optional
4481
+ Optional output array. Must be same `shape` and `dtype` as the expected
4482
+ output if `out` was not specified.
4483
+ dtype : dtype or str, optional
4484
+ Data type for computations can either be 'float32' or 'float64'. If
4485
+ `out` is specified, the data type of `out` is used and this argument is
4486
+ ignored. If `out` is not provided, 'float64' is used by default.
4487
+
4488
+ Returns
4489
+ -------
4490
+ ndarray
4491
+ Normalized device or viewport coordinates [x, y, z] of the point. The
4492
+ `z` component is similar to the depth buffer value for the object point.
4493
+
4494
+ """
4495
+ if out is None:
4496
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4497
+ else:
4498
+ dtype = np.dtype(dtype).type
4499
+
4500
+ toReturn = np.zeros_like(objPos, dtype=dtype) if out is None else out
4501
+ winCoord, objPos = np.atleast_2d(toReturn, objPos)
4502
+
4503
+ # transformation matrix
4504
+ mvp = np.matmul(proj, modelView)
4505
+
4506
+ # must have `w` for this one
4507
+ if objPos.shape[1] == 3:
4508
+ temp = np.zeros((objPos.shape[1], 4), dtype=dtype)
4509
+ temp[:, :3] = objPos
4510
+ objPos = temp
4511
+
4512
+ # transform the points
4513
+ objNorm = applyMatrix(mvp, objPos, dtype=dtype)
4514
+
4515
+ if viewport is not None:
4516
+ # if we have a viewport, transform it
4517
+ objNorm[:, :] += 1.0
4518
+ winCoord[:, 0] = viewport[0] + viewport[2] * objNorm[:, 0]
4519
+ winCoord[:, 1] = viewport[1] + viewport[3] * objNorm[:, 1]
4520
+ winCoord[:, 2] = objNorm[:, 2]
4521
+ winCoord[:, :] /= 2.0
4522
+ else:
4523
+ # already in NDC
4524
+ winCoord[:, :] = objNorm
4525
+
4526
+ return toReturn # ref to winCoord
4527
+
4528
+
4529
+ def reverseProject(winPos, modelView, proj, viewport=None, out=None, dtype=None):
4530
+ """Unproject window coordinates into object or scene coordinates.
4531
+
4532
+ This function works like `gluUnProject` and can be used to find to an object
4533
+ or scene coordinate at the point on-screen (mouse coordinate or pixel). The
4534
+ coordinate can then be used to create a direction vector from the viewer's
4535
+ eye location. Another use of this function is to convert depth buffer
4536
+ samples to object or scene coordinates. This is the inverse operation of
4537
+ :func:`forwardProject`.
4538
+
4539
+ Parameters
4540
+ ----------
4541
+ winPos : array_like
4542
+ Window coordinates (x, y, z). If `viewport` is not specified, these
4543
+ should be normalized device coordinates. If an Nx3 array of coordinates
4544
+ is specified, where each row contains a window coordinate this function
4545
+ will return an array of unprojected coordinates with the same size.
4546
+ Usually, you only need to specify the `x` and `y` coordinate, leaving
4547
+ `z` as zero. However, you can specify `z` if sampling from a depth map
4548
+ or buffer to convert a depth sample to an actual location.
4549
+ modelView : array_like
4550
+ 4x4 combined model and view matrix for returned value to be object
4551
+ coordinates. Specify only the view matrix for a coordinate in the scene.
4552
+ proj : array_like
4553
+ 4x4 projection matrix used for rendering.
4554
+ viewport : array_like
4555
+ Viewport rectangle for the window [x, y, w, h]. Do not specify one if
4556
+ `winPos` is in already in normalized device coordinates.
4557
+ out : ndarray, optional
4558
+ Optional output array. Must be same `shape` and `dtype` as the expected
4559
+ output if `out` was not specified.
4560
+ dtype : dtype or str, optional
4561
+ Data type for computations can either be 'float32' or 'float64'. If
4562
+ `out` is specified, the data type of `out` is used and this argument is
4563
+ ignored. If `out` is not provided, 'float64' is used by default.
4564
+
4565
+ Returns
4566
+ -------
4567
+ ndarray
4568
+ Object or scene coordinates.
4569
+
4570
+ """
4571
+ if out is None:
4572
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4573
+ else:
4574
+ dtype = np.dtype(dtype).type
4575
+
4576
+ toReturn = np.zeros_like(winPos, dtype=dtype) if out is None else out
4577
+ objCoord, winPos = np.atleast_2d(toReturn, winPos)
4578
+
4579
+ # get inverse of model and projection matrix
4580
+ invMVP = np.linalg.inv(np.matmul(proj, modelView))
4581
+
4582
+ if viewport is not None:
4583
+ # if we have a viewport, we need to transform to NDC first
4584
+ objCoord[:, 0] = ((2 * winPos[:, 0] - viewport[0]) / viewport[2])
4585
+ objCoord[:, 1] = ((2 * winPos[:, 1] - viewport[1]) / viewport[3])
4586
+ objCoord[:, 2] = 2 * winPos[:, 2]
4587
+ objCoord -= 1
4588
+ objCoord[:, :] = applyMatrix(invMVP, objCoord, dtype=dtype)
4589
+ else:
4590
+ # already in NDC, just apply
4591
+ objCoord[:, :] = applyMatrix(invMVP, winPos, dtype=dtype)
4592
+
4593
+ return toReturn # ref to objCoord
4594
+
4595
+
4596
+ def lookAt(eyePos, centerPos, upVec=(0.0, 1.0, 0.0), out=None, dtype=None):
4597
+ """Create a transformation matrix to orient a view towards some point.
4598
+
4599
+ Based on the same algorithm as 'gluLookAt'. This does not generate a
4600
+ projection matrix, but rather the matrix to transform the observer's view in
4601
+ the scene.
4602
+
4603
+ For more information see:
4604
+ https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml
4605
+
4606
+ Parameters
4607
+ ----------
4608
+ eyePos : list of float or ndarray
4609
+ Eye position in the scene.
4610
+ centerPos : list of float or ndarray
4611
+ Position of the object center in the scene.
4612
+ upVec : list of float or ndarray, optional
4613
+ Vector defining the up vector. Default is +Y is up.
4614
+ out : ndarray, optional
4615
+ Optional output array. Must be same `shape` and `dtype` as the expected
4616
+ output if `out` was not specified.
4617
+ dtype : dtype or str, optional
4618
+ Data type for arrays, can either be 'float32' or 'float64'. If `None` is
4619
+ specified, the data type is inferred by `out`. If `out` is not provided,
4620
+ the default is 'float64'.
4621
+
4622
+ Returns
4623
+ -------
4624
+ ndarray
4625
+ 4x4 view matrix
4626
+
4627
+ Notes
4628
+ -----
4629
+ * This function was moved from `viewtools` in version 2025.1.0.
4630
+ * The returned matrix is row-major. Values are floats with 32-bits of
4631
+ precision stored as a contiguous (C-order) array.
4632
+
4633
+ """
4634
+ if out is None:
4635
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4636
+ else:
4637
+ dtype = np.dtype(out.dtype).type
4638
+
4639
+ toReturn = np.zeros((4, 4,), dtype=dtype) if out is None else out
4640
+ if out is not None:
4641
+ toReturn.fill(0.0)
4642
+
4643
+ eyePos = np.asarray(eyePos, dtype=dtype)
4644
+ centerPos = np.asarray(centerPos, dtype=dtype)
4645
+ upVec = np.asarray(upVec, dtype=dtype)
4646
+
4647
+ f = centerPos - eyePos
4648
+ f /= np.linalg.norm(f)
4649
+ upVec /= np.linalg.norm(upVec)
4650
+
4651
+ s = np.cross(f, upVec)
4652
+ u = np.cross(s / np.linalg.norm(s), f)
4653
+
4654
+ rotMat = np.zeros((4, 4), dtype=dtype)
4655
+ rotMat[0, :3] = s
4656
+ rotMat[1, :3] = u
4657
+ rotMat[2, :3] = -f
4658
+ rotMat[3, 3] = 1.0
4659
+
4660
+ transMat = np.identity(4, dtype=dtype)
4661
+ transMat[:3, 3] = -eyePos
4662
+
4663
+ return np.matmul(rotMat, transMat, out=toReturn)
4664
+
4665
+
4666
+ # ------------------------------------------------------------------------------
4667
+ # Misc. Math Functions
4668
+ #
4669
+
4670
+ def zeroFix(a, inplace=False, threshold=None):
4671
+ """Fix zeros in an array.
4672
+
4673
+ This function truncates very small numbers in an array to zero and removes
4674
+ any negative zeros.
4675
+
4676
+ Parameters
4677
+ ----------
4678
+ a : ndarray
4679
+ Input array, must be a Numpy array.
4680
+ inplace : bool
4681
+ Fix an array inplace. If `True`, the input array will be modified,
4682
+ otherwise a new array will be returned with same `dtype` and shape with
4683
+ the fixed values.
4684
+ threshold : float or None
4685
+ Threshold for truncation. If `None`, the machine epsilon value for the
4686
+ input array `dtype` will be used. You can specify a custom threshold as
4687
+ a float.
4688
+
4689
+ Returns
4690
+ -------
4691
+ ndarray
4692
+ Output array with zeros fixed.
4693
+
4694
+ """
4695
+ toReturn = np.copy(a) if not inplace else a
4696
+ toReturn += 0.0 # remove negative zeros
4697
+ threshold = np.finfo(a.dtype).eps if threshold is None else float(threshold)
4698
+ toReturn[np.abs(toReturn) < threshold] = 0.0 # make zero
4699
+
4700
+ return toReturn
4701
+
4702
+
4703
+ def lensCorrection(xys, coefK=(1.0,), distCenter=(0., 0.), out=None,
4704
+ dtype=None):
4705
+ """Lens correction (or distortion) using the division model with even
4706
+ polynomial terms.
4707
+
4708
+ Calculate new vertex positions or texture coordinates to apply radial
4709
+ warping, such as 'pincushion' and 'barrel' distortion. This is to compensate
4710
+ for optical distortion introduced by lenses placed in the optical path of
4711
+ the viewer and the display (such as in an HMD).
4712
+
4713
+ See references[1]_ for implementation details.
4714
+
4715
+ Parameters
4716
+ ----------
4717
+ xys : array_like
4718
+ Nx2 list of vertex positions or texture coordinates to distort. Works
4719
+ correctly only if input values range between -1.0 and 1.0.
4720
+ coefK : array_like or float
4721
+ Distortion coefficients K_n. Specifying multiple values will add more
4722
+ polynomial terms to the distortion formula. Positive values will produce
4723
+ 'barrel' distortion, whereas negative will produce 'pincushion'
4724
+ distortion. In most cases, two or three coefficients are adequate,
4725
+ depending on the degree of distortion.
4726
+ distCenter : array_like, optional
4727
+ X and Y coordinate of the distortion center (eg. (0.2, -0.4)).
4728
+ out : ndarray, optional
4729
+ Optional output array. Must be same `shape` and `dtype` as the expected
4730
+ output if `out` was not specified.
4731
+ dtype : dtype or str, optional
4732
+ Data type for computations can either be 'float32' or 'float64'. If
4733
+ `out` is specified, the data type of `out` is used and this argument is
4734
+ ignored. If `out` is not provided, 'float64' is used by default.
4735
+
4736
+ Returns
4737
+ -------
4738
+ ndarray
4739
+ Array of distorted vertices.
4740
+
4741
+ Notes
4742
+ -----
4743
+ * At this time tangential distortion (i.e. due to a slant in the display)
4744
+ cannot be corrected for.
4745
+
4746
+ References
4747
+ ----------
4748
+ .. [1] Fitzgibbon, W. (2001). Simultaneous linear estimation of multiple
4749
+ view geometry and lens distortion. Proceedings of the 2001 IEEE Computer
4750
+ Society Conference on Computer Vision and Pattern Recognition (CVPR).
4751
+ IEEE.
4752
+
4753
+ Examples
4754
+ --------
4755
+ Creating a lens correction mesh with barrel distortion (eg. for HMDs)::
4756
+
4757
+ vertices, textureCoords, normals, faces = gltools.createMeshGrid(
4758
+ subdiv=11, tessMode='center')
4759
+
4760
+ # recompute vertex positions
4761
+ vertices[:, :2] = mt.lensCorrection(vertices[:, :2], coefK=(5., 5.))
4762
+
4763
+ """
4764
+ if out is None:
4765
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4766
+ else:
4767
+ dtype = np.dtype(dtype).type
4768
+
4769
+ if isinstance(coefK, (float, int,)):
4770
+ coefK = (coefK,)
4771
+
4772
+ xys = np.asarray(xys, dtype=dtype)
4773
+ coefK = np.asarray(coefK, dtype=dtype)
4774
+
4775
+ d_minus_c = xys - np.asarray(distCenter, dtype=dtype)
4776
+ r = np.power(length(d_minus_c, dtype=dtype)[:, np.newaxis],
4777
+ np.arange(len(coefK), dtype=dtype) * 2. + 2.)
4778
+
4779
+ toReturn = np.zeros_like(xys, dtype=dtype) if out is None else out
4780
+
4781
+ denom = dtype(1.0) + dot(coefK, r, dtype=dtype)
4782
+ toReturn[:, :] = xys + (d_minus_c / denom[:, np.newaxis])
4783
+
4784
+ return toReturn
4785
+
4786
+
4787
+ def lensCorrectionSpherical(xys, coefK=1.0, aspect=1.0, out=None, dtype=None):
4788
+ """Simple lens correction.
4789
+
4790
+ Lens correction for a spherical lenses with distortion centered at the
4791
+ middle of the display. See references[1]_ for implementation details.
4792
+
4793
+ Parameters
4794
+ ----------
4795
+ xys : array_like
4796
+ Nx2 list of vertex positions or texture coordinates to distort. Assumes
4797
+ the output will be rendered to normalized device coordinates where
4798
+ points range from -1.0 to 1.0.
4799
+ coefK : float
4800
+ Distortion coefficient. Use positive numbers for pincushion distortion
4801
+ and negative for barrel distortion.
4802
+ aspect : float
4803
+ Aspect ratio of the target window or buffer (width / height).
4804
+ out : ndarray, optional
4805
+ Optional output array. Must be same `shape` and `dtype` as the expected
4806
+ output if `out` was not specified.
4807
+ dtype : dtype or str, optional
4808
+ Data type for computations can either be 'float32' or 'float64'. If
4809
+ `out` is specified, the data type of `out` is used and this argument is
4810
+ ignored. If `out` is not provided, 'float64' is used by default.
4811
+
4812
+ Returns
4813
+ -------
4814
+ ndarray
4815
+ Array of distorted vertices.
4816
+
4817
+ References
4818
+ ----------
4819
+ .. [1] Lens Distortion White Paper, Andersson Technologies LLC,
4820
+ www.ssontech.com/content/lensalg.html (obtained 07/28/2020)
4821
+
4822
+ Examples
4823
+ --------
4824
+ Creating a lens correction mesh with barrel distortion (eg. for HMDs)::
4825
+
4826
+ vertices, textureCoords, normals, faces = gltools.createMeshGrid(
4827
+ subdiv=11, tessMode='center')
4828
+
4829
+ # recompute vertex positions
4830
+ vertices[:, :2] = mt.lensCorrection2(vertices[:, :2], coefK=2.0)
4831
+
4832
+ """
4833
+ if out is None:
4834
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
4835
+ else:
4836
+ dtype = np.dtype(dtype).type
4837
+
4838
+ toReturn = np.empty_like(xys, dtype=dtype) if out is None else out
4839
+
4840
+ xys = np.asarray(xys, dtype=dtype)
4841
+ toReturn[:, 0] = u = xys[:, 0]
4842
+ toReturn[:, 1] = v = xys[:, 1]
4843
+ coefKCubed = np.power(coefK, 3, dtype=dtype)
4844
+
4845
+ r2 = aspect * aspect * u * u + v * v
4846
+ r2sqr = np.sqrt(r2, dtype=dtype)
4847
+ f = 1. + r2 * (coefK + coefKCubed * r2sqr)
4848
+
4849
+ toReturn[:, 0] *= f
4850
+ toReturn[:, 1] *= f
4851
+
4852
+ return toReturn
4853
+
4854
+
4855
+ class infrange():
4856
+ """
4857
+ Similar to base Python `range`, but allowing the step to be a float or even
4858
+ 0, useful for specifying ranges for logical comparisons.
4859
+ """
4860
+ def __init__(self, min, max, step=0):
4861
+ self.min = min
4862
+ self.max = max
4863
+ self.step = step
4864
+
4865
+ @property
4866
+ def range(self):
4867
+ return abs(self.max-self.min)
4868
+
4869
+ def __lt__(self, other):
4870
+ return other > self.max
4871
+
4872
+ def __le__(self, other):
4873
+ return other > self.min
4874
+
4875
+ def __gt__(self, other):
4876
+ return self.min > other
4877
+
4878
+ def __ge__(self, other):
4879
+ return self.max > other
4880
+
4881
+ def __contains__(self, item):
4882
+ if self.step == 0:
4883
+ return self.min < item < self.max
4884
+ else:
4885
+ return item in np.linspace(self.min, self.max, int(self.range/self.step)+1)
4886
+
4887
+ def __eq__(self, item):
4888
+ if isinstance(item, self.__class__):
4889
+ return all((
4890
+ self.min == item.min,
4891
+ self.max == item.max,
4892
+ self.step == item.step
4893
+ ))
4894
+ return item in self
4895
+
4896
+ def __add__(self, other):
4897
+ return self.__class__(self.min+other, self.max+other, self.step)
4898
+
4899
+ def __sub__(self, other):
4900
+ return self.__class__(self.min - other, self.max - other, self.step)
4901
+
4902
+ def __mul__(self, other):
4903
+ return self.__class__(self.min * other, self.max * other, self.step * other)
4904
+
4905
+ def __truedic__(self, other):
4906
+ return self.__class__(self.min / other, self.max / other, self.step / other)
4907
+
4908
+
4909
+ if __name__ == "__main__":
4910
+ pass