psychopy 2024.2.5__py3-none-any.whl → 2025.1.1__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.

Potentially problematic release.


This version of psychopy might be problematic. Click here for more details.

Files changed (863) hide show
  1. psychopy/CHANGELOG.txt +4 -4
  2. psychopy/GIT_SHA +1 -1
  3. psychopy/LICENSE.txt +1 -1
  4. psychopy/VERSION +1 -1
  5. psychopy/__init__.py +10 -7
  6. psychopy/alerts/__init__.py +1 -1
  7. psychopy/alerts/_alerts.py +53 -17
  8. psychopy/alerts/_errorHandler.py +3 -4
  9. psychopy/alerts/alertsCatalogue/3210.yaml +27 -0
  10. psychopy/alerts/alertsCatalogue/3610.yaml +19 -0
  11. psychopy/alerts/alertsCatalogue/4130.yaml +19 -0
  12. psychopy/alerts/alertsCatalogue/alertCategories.yaml +8 -1
  13. psychopy/alerts/alertsCatalogue/alertmsg.py +1 -1
  14. psychopy/alerts/alerttools.py +0 -16
  15. psychopy/app/Resources/betasplash.png +0 -0
  16. psychopy/app/Resources/betasplash@2x.png +0 -0
  17. psychopy/app/Resources/classic/case.png +0 -0
  18. psychopy/app/Resources/classic/case@2x.png +0 -0
  19. psychopy/app/Resources/classic/fileaudio.png +0 -0
  20. psychopy/app/Resources/classic/fileaudio@2x.png +0 -0
  21. psychopy/app/Resources/classic/filecss.png +0 -0
  22. psychopy/app/Resources/classic/filecss@2x.png +0 -0
  23. psychopy/app/Resources/classic/filecsv.png +0 -0
  24. psychopy/app/Resources/classic/filecsv@2x.png +0 -0
  25. psychopy/app/Resources/classic/filedesign.png +0 -0
  26. psychopy/app/Resources/classic/filedesign@2x.png +0 -0
  27. psychopy/app/Resources/classic/filefont.png +0 -0
  28. psychopy/app/Resources/classic/filefont@2x.png +0 -0
  29. psychopy/app/Resources/classic/filegit.png +0 -0
  30. psychopy/app/Resources/classic/filegit@2x.png +0 -0
  31. psychopy/app/Resources/classic/filehtml.png +0 -0
  32. psychopy/app/Resources/classic/filehtml@2x.png +0 -0
  33. psychopy/app/Resources/classic/fileimage.png +0 -0
  34. psychopy/app/Resources/classic/fileimage@2x.png +0 -0
  35. psychopy/app/Resources/classic/fileinfo.png +0 -0
  36. psychopy/app/Resources/classic/fileinfo@2x.png +0 -0
  37. psychopy/app/Resources/classic/filejs.png +0 -0
  38. psychopy/app/Resources/classic/filejs@2x.png +0 -0
  39. psychopy/app/Resources/classic/filejson.png +0 -0
  40. psychopy/app/Resources/classic/filejson@2x.png +0 -0
  41. psychopy/app/Resources/classic/filepkg.png +0 -0
  42. psychopy/app/Resources/classic/filepkg@2x.png +0 -0
  43. psychopy/app/Resources/classic/filepsyexp.png +0 -0
  44. psychopy/app/Resources/classic/filepsyexp@2x.png +0 -0
  45. psychopy/app/Resources/classic/filepy.png +0 -0
  46. psychopy/app/Resources/classic/filepy@2x.png +0 -0
  47. psychopy/app/Resources/classic/filetxt.png +0 -0
  48. psychopy/app/Resources/classic/filetxt@2x.png +0 -0
  49. psychopy/app/Resources/classic/fileunknown.png +0 -0
  50. psychopy/app/Resources/classic/fileunknown@2x.png +0 -0
  51. psychopy/app/Resources/classic/filevideo.png +0 -0
  52. psychopy/app/Resources/classic/filevideo@2x.png +0 -0
  53. psychopy/app/Resources/classic/find.png +0 -0
  54. psychopy/app/Resources/classic/find@2x.png +0 -0
  55. psychopy/app/Resources/classic/loop.png +0 -0
  56. psychopy/app/Resources/classic/loop@2x.png +0 -0
  57. psychopy/app/Resources/classic/regex.png +0 -0
  58. psychopy/app/Resources/classic/regex@2x.png +0 -0
  59. psychopy/app/Resources/dark/case.png +0 -0
  60. psychopy/app/Resources/dark/case@2x.png +0 -0
  61. psychopy/app/Resources/dark/fileaudio.png +0 -0
  62. psychopy/app/Resources/dark/fileaudio@2x.png +0 -0
  63. psychopy/app/Resources/dark/filecss.png +0 -0
  64. psychopy/app/Resources/dark/filecss@2x.png +0 -0
  65. psychopy/app/Resources/dark/filecsv.png +0 -0
  66. psychopy/app/Resources/dark/filecsv@2x.png +0 -0
  67. psychopy/app/Resources/dark/filedesign.png +0 -0
  68. psychopy/app/Resources/dark/filedesign@2x.png +0 -0
  69. psychopy/app/Resources/dark/filefont.png +0 -0
  70. psychopy/app/Resources/dark/filefont@2x.png +0 -0
  71. psychopy/app/Resources/dark/filegit.png +0 -0
  72. psychopy/app/Resources/dark/filegit@2x.png +0 -0
  73. psychopy/app/Resources/dark/filehtml.png +0 -0
  74. psychopy/app/Resources/dark/filehtml@2x.png +0 -0
  75. psychopy/app/Resources/dark/fileimage.png +0 -0
  76. psychopy/app/Resources/dark/fileimage@2x.png +0 -0
  77. psychopy/app/Resources/dark/fileinfo.png +0 -0
  78. psychopy/app/Resources/dark/fileinfo@2x.png +0 -0
  79. psychopy/app/Resources/dark/filejs.png +0 -0
  80. psychopy/app/Resources/dark/filejs@2x.png +0 -0
  81. psychopy/app/Resources/dark/filejson.png +0 -0
  82. psychopy/app/Resources/dark/filejson@2x.png +0 -0
  83. psychopy/app/Resources/dark/filepkg.png +0 -0
  84. psychopy/app/Resources/dark/filepkg@2x.png +0 -0
  85. psychopy/app/Resources/dark/filepsyexp.png +0 -0
  86. psychopy/app/Resources/dark/filepsyexp@2x.png +0 -0
  87. psychopy/app/Resources/dark/filepy.png +0 -0
  88. psychopy/app/Resources/dark/filepy@2x.png +0 -0
  89. psychopy/app/Resources/dark/filetxt.png +0 -0
  90. psychopy/app/Resources/dark/filetxt@2x.png +0 -0
  91. psychopy/app/Resources/dark/fileunknown.png +0 -0
  92. psychopy/app/Resources/dark/fileunknown@2x.png +0 -0
  93. psychopy/app/Resources/dark/filevideo.png +0 -0
  94. psychopy/app/Resources/dark/filevideo@2x.png +0 -0
  95. psychopy/app/Resources/dark/find.png +0 -0
  96. psychopy/app/Resources/dark/find@2x.png +0 -0
  97. psychopy/app/Resources/dark/loop.png +0 -0
  98. psychopy/app/Resources/dark/loop@2x.png +0 -0
  99. psychopy/app/Resources/dark/regex.png +0 -0
  100. psychopy/app/Resources/dark/regex@2x.png +0 -0
  101. psychopy/app/Resources/light/case.png +0 -0
  102. psychopy/app/Resources/light/case@2x.png +0 -0
  103. psychopy/app/Resources/light/fileaudio.png +0 -0
  104. psychopy/app/Resources/light/fileaudio@2x.png +0 -0
  105. psychopy/app/Resources/light/filecss.png +0 -0
  106. psychopy/app/Resources/light/filecss@2x.png +0 -0
  107. psychopy/app/Resources/light/filecsv.png +0 -0
  108. psychopy/app/Resources/light/filecsv@2x.png +0 -0
  109. psychopy/app/Resources/light/filedesign.png +0 -0
  110. psychopy/app/Resources/light/filedesign@2x.png +0 -0
  111. psychopy/app/Resources/light/filefont.png +0 -0
  112. psychopy/app/Resources/light/filefont@2x.png +0 -0
  113. psychopy/app/Resources/light/filegit.png +0 -0
  114. psychopy/app/Resources/light/filegit@2x.png +0 -0
  115. psychopy/app/Resources/light/filehtml.png +0 -0
  116. psychopy/app/Resources/light/filehtml@2x.png +0 -0
  117. psychopy/app/Resources/light/fileimage.png +0 -0
  118. psychopy/app/Resources/light/fileimage@2x.png +0 -0
  119. psychopy/app/Resources/light/fileinfo.png +0 -0
  120. psychopy/app/Resources/light/fileinfo@2x.png +0 -0
  121. psychopy/app/Resources/light/filejs.png +0 -0
  122. psychopy/app/Resources/light/filejs@2x.png +0 -0
  123. psychopy/app/Resources/light/filejson.png +0 -0
  124. psychopy/app/Resources/light/filejson@2x.png +0 -0
  125. psychopy/app/Resources/light/filepkg.png +0 -0
  126. psychopy/app/Resources/light/filepkg@2x.png +0 -0
  127. psychopy/app/Resources/light/filepsyexp.png +0 -0
  128. psychopy/app/Resources/light/filepsyexp@2x.png +0 -0
  129. psychopy/app/Resources/light/filepy.png +0 -0
  130. psychopy/app/Resources/light/filepy@2x.png +0 -0
  131. psychopy/app/Resources/light/filetxt.png +0 -0
  132. psychopy/app/Resources/light/filetxt@2x.png +0 -0
  133. psychopy/app/Resources/light/fileunknown.png +0 -0
  134. psychopy/app/Resources/light/fileunknown@2x.png +0 -0
  135. psychopy/app/Resources/light/filevideo.png +0 -0
  136. psychopy/app/Resources/light/filevideo@2x.png +0 -0
  137. psychopy/app/Resources/light/find.png +0 -0
  138. psychopy/app/Resources/light/find@2x.png +0 -0
  139. psychopy/app/Resources/light/loop.png +0 -0
  140. psychopy/app/Resources/light/loop@2x.png +0 -0
  141. psychopy/app/Resources/light/regex.png +0 -0
  142. psychopy/app/Resources/light/regex@2x.png +0 -0
  143. psychopy/app/Resources/routine_templates/Basic.psyexp +0 -1
  144. psychopy/app/Resources/routine_templates/Misc.psyexp +0 -1
  145. psychopy/app/Resources/routine_templates/Online.psyexp +0 -2
  146. psychopy/app/Resources/routine_templates/Trials.psyexp +0 -1
  147. psychopy/app/Resources/splash.png +0 -0
  148. psychopy/app/Resources/splash@2x.png +0 -0
  149. psychopy/app/__init__.py +49 -8
  150. psychopy/app/__main__.py +3 -0
  151. psychopy/app/_psychopyApp.py +134 -125
  152. psychopy/app/builder/builder.py +42 -22
  153. psychopy/app/builder/dialogs/__init__.py +44 -11
  154. psychopy/app/builder/dialogs/dlgsCode.py +1 -1
  155. psychopy/app/builder/dialogs/dlgsConditions.py +1 -1
  156. psychopy/app/builder/dialogs/findDlg.py +106 -20
  157. psychopy/app/builder/dialogs/paramCtrls.py +42 -1
  158. psychopy/app/builder/validators.py +1 -1
  159. psychopy/app/coder/codeEditorBase.py +8 -8
  160. psychopy/app/coder/coder.py +32 -29
  161. psychopy/app/coder/fileBrowser.py +68 -22
  162. psychopy/app/coder/folding.py +1 -1
  163. psychopy/app/coder/psychoParser.py +1 -1
  164. psychopy/app/coder/repl.py +1 -1
  165. psychopy/app/coder/sourceTree.py +1 -1
  166. psychopy/app/connections/__init__.py +1 -1
  167. psychopy/app/connections/news.py +1 -1
  168. psychopy/app/connections/sendusage.py +1 -1
  169. psychopy/app/connections/updates.py +1 -1
  170. psychopy/app/console.py +1 -1
  171. psychopy/app/errorDlg.py +1 -1
  172. psychopy/app/frametracker.py +1 -1
  173. psychopy/app/idle.py +1 -1
  174. psychopy/app/jobs.py +5 -2
  175. psychopy/app/linuxconfig/__init__.py +1 -1
  176. psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
  177. psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +4 -4
  178. psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
  179. psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
  180. psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
  181. psychopy/app/locale/de_DE/LC_MESSAGE/messages.po +3 -3
  182. psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
  183. psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
  184. psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
  185. psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
  186. psychopy/app/locale/es_CO/LC_MESSAGE/messages.po +4 -4
  187. psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
  188. psychopy/app/locale/es_ES/LC_MESSAGE/messages.po +4 -4
  189. psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
  190. psychopy/app/locale/es_US/LC_MESSAGE/messages.po +4 -4
  191. psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
  192. psychopy/app/locale/et_EE/LC_MESSAGE/messages.po +2 -2
  193. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
  194. psychopy/app/locale/fa_IR/LC_MESSAGE/messages.po +1 -1
  195. psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
  196. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
  197. psychopy/app/locale/fr_FR/LC_MESSAGE/messages.po +2 -2
  198. psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
  199. psychopy/app/locale/he_IL/LC_MESSAGE/messages.po +2 -2
  200. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
  201. psychopy/app/locale/hi_IN/LC_MESSAGE/messages.po +2 -2
  202. psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
  203. psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
  204. psychopy/app/locale/it_IT/LC_MESSAGE/messages.po +2 -2
  205. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
  206. psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +3421 -2396
  207. psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
  208. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
  209. psychopy/app/locale/ms_MY/LC_MESSAGE/messages.po +2 -2
  210. psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
  211. psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
  212. psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
  213. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
  214. psychopy/app/locale/pt_PT/LC_MESSAGE/messages.po +4 -4
  215. psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
  216. psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
  217. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
  218. psychopy/app/locale/sv_SE/LC_MESSAGE/messages.po +2 -2
  219. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
  220. psychopy/app/locale/tr_TR/LC_MESSAGE/messages.po +2 -2
  221. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
  222. psychopy/app/locale/zh_CN/LC_MESSAGE/messages.po +3 -3
  223. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
  224. psychopy/app/locale/zh_TW/LC_MESSAGE/messages.po +2 -2
  225. psychopy/app/{builder/localizedStrings.py → localizedStrings.py} +173 -26
  226. psychopy/app/pavlovia_ui/__init__.py +1 -1
  227. psychopy/app/pavlovia_ui/_base.py +1 -1
  228. psychopy/app/pavlovia_ui/functions.py +1 -1
  229. psychopy/app/pavlovia_ui/menu.py +1 -1
  230. psychopy/app/pavlovia_ui/project.py +1 -1
  231. psychopy/app/pavlovia_ui/search.py +1 -1
  232. psychopy/app/pavlovia_ui/sync.py +1 -1
  233. psychopy/app/pavlovia_ui/user.py +1 -1
  234. psychopy/app/plugin_manager/dialog.py +34 -96
  235. psychopy/app/plugin_manager/packages.py +10 -14
  236. psychopy/app/plugin_manager/plugins.py +64 -4
  237. psychopy/app/preferencesDlg.py +12 -37
  238. psychopy/app/psychopyApp.py +130 -44
  239. psychopy/app/ribbon.py +1 -0
  240. psychopy/app/runner/runner.py +19 -7
  241. psychopy/app/runner/scriptProcess.py +11 -6
  242. psychopy/app/stdout/stdOutRich.py +9 -2
  243. psychopy/app/themes/fonts.py +1 -1
  244. psychopy/app/themes/icons.py +2 -38
  245. psychopy/app/ui/__init__.py +1 -1
  246. psychopy/app/utils.py +3 -3
  247. psychopy/assets/default.mp3 +0 -0
  248. psychopy/assets/fonts/NotoSans-Bold.ttf +0 -0
  249. psychopy/assets/fonts/NotoSans-BoldItalic.ttf +0 -0
  250. psychopy/assets/fonts/NotoSans-Italic.ttf +0 -0
  251. psychopy/assets/fonts/NotoSans-Regular.ttf +0 -0
  252. psychopy/assets/voicekeyThresholdStim.wav +0 -0
  253. psychopy/clock.py +4 -1
  254. psychopy/colors.py +12 -5
  255. psychopy/core.py +1 -1
  256. psychopy/data/base.py +1 -1
  257. psychopy/data/experiment.py +151 -46
  258. psychopy/data/routine.py +27 -1
  259. psychopy/data/staircase.py +2 -1
  260. psychopy/data/trial.py +62 -14
  261. psychopy/data/utils.py +2 -2
  262. psychopy/demos/builder/Design Templates/branchedExperiment/branchedExperiment.psyexp +333 -218
  263. psychopy/demos/builder/Design Templates/psychophysicsStaircase/psychophysicsStaircase.psyexp +261 -239
  264. psychopy/demos/builder/Design Templates/psychophysicsStairsInterleaved/psychophysicsStaircaseInterleaved.psyexp +319 -180
  265. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.psyexp +204 -116
  266. psychopy/demos/builder/Experiments/BART/assets/background.jpg +0 -0
  267. psychopy/demos/builder/Experiments/BART/assets/blueBalloon.png +0 -0
  268. psychopy/demos/builder/Experiments/BART/assets/greenBalloon.png +0 -0
  269. psychopy/demos/builder/Experiments/BART/assets/redBalloon.png +0 -0
  270. psychopy/demos/builder/Experiments/BART/bart.psyexp +779 -866
  271. psychopy/demos/builder/Experiments/BigFiveInventory/BFI.psyexp +242 -180
  272. psychopy/demos/builder/Experiments/GoNoGo/gng.psyexp +419 -406
  273. psychopy/demos/builder/Experiments/dragAndDrop/README.md +2 -37
  274. psychopy/demos/builder/Experiments/dragAndDrop/drag_and_drop.psyexp +460 -1204
  275. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/blank_grid.png +0 -0
  276. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/make_shapes.psyexp +221 -0
  277. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/readme.md +4 -0
  278. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_1.png +0 -0
  279. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_10.png +0 -0
  280. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_2.png +0 -0
  281. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_3.png +0 -0
  282. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_4.png +0 -0
  283. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_5.png +0 -0
  284. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_6.png +0 -0
  285. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_7.png +0 -0
  286. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_8.png +0 -0
  287. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solution_9.png +0 -0
  288. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/solutions.xlsx +0 -0
  289. psychopy/demos/builder/Experiments/mentalRotation/MentalRotation.psyexp +583 -542
  290. psychopy/demos/builder/Experiments/navon/NavonTask.psyexp +458 -427
  291. psychopy/demos/builder/Experiments/sternberg/sternberg.psyexp +588 -550
  292. psychopy/demos/builder/Experiments/stroop/stroop.psyexp +303 -207
  293. psychopy/demos/builder/Experiments/stroopExtended/stroop.psyexp +390 -215
  294. psychopy/demos/builder/Experiments/stroopExtended/stroopReverse.psyexp +390 -215
  295. psychopy/demos/builder/Experiments/stroopVoice/stroopVoice.psyexp +357 -331
  296. psychopy/demos/builder/Feature Demos/buttonBox/buttonBoxDemo.psyexp +3 -2
  297. psychopy/demos/builder/Feature Demos/counterbalance/counterbalance.psyexp +287 -277
  298. psychopy/demos/builder/Feature Demos/gratings/gratings.psyexp +370 -320
  299. psychopy/demos/builder/Feature Demos/noise/noise.psyexp +452 -399
  300. psychopy/demos/builder/Feature Demos/panorama/panorama.psyexp +168 -133
  301. psychopy/demos/builder/Feature Demos/pilotMode/pilotMode.psyexp +4 -3
  302. psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +420 -392
  303. psychopy/demos/builder/Feature Demos/sliders/sliders.psyexp +917 -871
  304. psychopy/demos/builder/Feature Demos/visualValidator/readme.md +7 -0
  305. psychopy/demos/builder/Feature Demos/visualValidator/visualValidator.psyexp +200 -0
  306. psychopy/demos/builder/Hardware/EEG_parallel_component/EEG_triggers_parallel_comp.psyexp +25 -9
  307. psychopy/demos/builder/Hardware/EEG_serial_code/EEG_triggers_serial_code.psyexp +372 -361
  308. psychopy/demos/builder/Hardware/EEG_serial_component/EEG_triggers_serial_comp.psyexp +25 -9
  309. psychopy/demos/builder/Hardware/EGI_netstation/stroop.psyexp +320 -235
  310. psychopy/demos/builder/Hardware/Eyetracking_visual_search/visualSearch.psyexp +790 -651
  311. psychopy/demos/builder/Hardware/camera/camera.psyexp +326 -246
  312. psychopy/demos/builder/Hardware/eyetracking/eyetracking.psyexp +432 -327
  313. psychopy/demos/builder/Hardware/eyetracking_custom_cal/eyetracking_custom_cal.psyexp +440 -321
  314. psychopy/demos/builder/Hardware/fMRI/fMRI_demo.psyexp +190 -181
  315. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo.psyexp +323 -312
  316. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy.psyexp +360 -0
  317. psychopy/demos/builder/Hardware/lab_streaming_layer/lsl_triggers_demo_legacy_legacy.psyexp +312 -0
  318. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy.psyexp +329 -273
  319. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy.psyexp +360 -0
  320. psychopy/demos/builder/Hardware/lab_streaming_layer_legacy/lsl_triggers_demo_legacy_legacy_legacy.psyexp +312 -0
  321. psychopy/demos/builder/Hardware/microphone/microphone.psyexp +450 -404
  322. psychopy/demos/builder/Hardware/pump/pump.psyexp +593 -329
  323. psychopy/demos/builder/Helper Tools/achorVSalignment/FlowCircular-Regular.ttf +0 -0
  324. psychopy/demos/builder/Helper Tools/achorVSalignment/anchorAlignment.psyexp +336 -274
  325. psychopy/demos/builder/Helper Tools/clockFace/clockFace.psyexp +200 -149
  326. psychopy/demos/builder/Helper Tools/colors/colors.psyexp +300 -279
  327. psychopy/demos/builder/Helper Tools/drawPolygon/drawPolygon.psyexp +677 -564
  328. psychopy/demos/builder/Helper Tools/keyNameFinder/keyNameFinder.psyexp +214 -158
  329. psychopy/demos/builder/Helper Tools/spatialUnits/unitDemo.psyexp +195 -146
  330. psychopy/demos/coder/experiment control/runtimeInfo.py +1 -1
  331. psychopy/devices/__init__.py +1 -1
  332. psychopy/event.py +1 -1
  333. psychopy/exceptions.py +1 -1
  334. psychopy/experiment/__init__.py +1 -1
  335. psychopy/experiment/_experiment.py +30 -9
  336. psychopy/experiment/components/__init__.py +1 -6
  337. psychopy/experiment/components/_base.py +44 -19
  338. psychopy/experiment/components/aperture/__init__.py +1 -1
  339. psychopy/experiment/components/brush/__init__.py +2 -2
  340. psychopy/experiment/components/button/__init__.py +24 -28
  341. psychopy/experiment/components/camera/__init__.py +38 -39
  342. psychopy/experiment/components/code/__init__.py +1 -1
  343. psychopy/experiment/components/dots/__init__.py +1 -1
  344. psychopy/experiment/components/eyetracker_record/__init__.py +7 -3
  345. psychopy/experiment/components/form/__init__.py +3 -3
  346. psychopy/experiment/components/grating/__init__.py +1 -1
  347. psychopy/experiment/components/image/__init__.py +1 -1
  348. psychopy/experiment/components/joyButtons/__init__.py +2 -2
  349. psychopy/experiment/components/joyButtons/virtualJoyButtons.py +1 -1
  350. psychopy/experiment/components/joystick/__init__.py +3 -3
  351. psychopy/experiment/components/joystick/virtualJoystick.py +1 -1
  352. psychopy/experiment/components/keyboard/__init__.py +98 -122
  353. psychopy/experiment/components/microphone/__init__.py +54 -98
  354. psychopy/experiment/components/mouse/__init__.py +92 -93
  355. psychopy/experiment/components/movie/__init__.py +28 -50
  356. psychopy/experiment/components/parallelOut/__init__.py +3 -3
  357. psychopy/experiment/components/polygon/__init__.py +2 -3
  358. psychopy/experiment/components/roi/__init__.py +2 -2
  359. psychopy/experiment/components/routineSettings/__init__.py +2 -0
  360. psychopy/experiment/components/serialOut/__init__.py +7 -7
  361. psychopy/experiment/components/settings/__init__.py +317 -313
  362. psychopy/experiment/components/settings/eyetracking.py +108 -0
  363. psychopy/experiment/components/slider/__init__.py +5 -5
  364. psychopy/experiment/components/sound/__init__.py +168 -78
  365. psychopy/experiment/components/soundsensor/__init__.py +361 -0
  366. psychopy/experiment/components/soundsensor/classic/soundsensor.png +0 -0
  367. psychopy/experiment/components/soundsensor/classic/soundsensor@2x.png +0 -0
  368. psychopy/experiment/components/soundsensor/dark/soundsensor.png +0 -0
  369. psychopy/experiment/components/soundsensor/dark/soundsensor@2x.png +0 -0
  370. psychopy/experiment/components/soundsensor/light/soundsensor.png +0 -0
  371. psychopy/experiment/components/soundsensor/light/soundsensor@2x.png +0 -0
  372. psychopy/experiment/components/static/__init__.py +59 -33
  373. psychopy/experiment/components/text/__init__.py +2 -6
  374. psychopy/experiment/components/textbox/__init__.py +3 -7
  375. psychopy/experiment/components/unknown/__init__.py +2 -0
  376. psychopy/experiment/components/unknownPlugin/__init__.py +2 -0
  377. psychopy/experiment/exports.py +1 -1
  378. psychopy/experiment/flow.py +2 -2
  379. psychopy/experiment/localization.py +1 -1
  380. psychopy/experiment/loops.py +43 -10
  381. psychopy/experiment/params.py +6 -4
  382. psychopy/experiment/plugins.py +8 -1
  383. psychopy/experiment/py2js.py +1 -1
  384. psychopy/experiment/py2js_transpiler.py +1 -1
  385. psychopy/experiment/routines/__init__.py +1 -1
  386. psychopy/experiment/routines/_base.py +23 -24
  387. psychopy/experiment/routines/audioValidator/__init__.py +343 -0
  388. psychopy/experiment/routines/audioValidator/classic/audio_validator.png +0 -0
  389. psychopy/experiment/routines/audioValidator/classic/audio_validator@2x.png +0 -0
  390. psychopy/experiment/routines/audioValidator/dark/audio_validator.png +0 -0
  391. psychopy/experiment/routines/audioValidator/dark/audio_validator@2x.png +0 -0
  392. psychopy/experiment/routines/audioValidator/light/audio_validator.png +0 -0
  393. psychopy/experiment/routines/audioValidator/light/audio_validator@2x.png +0 -0
  394. psychopy/experiment/routines/eyetracker_calibrate/__init__.py +76 -17
  395. psychopy/experiment/routines/eyetracker_validate/__init__.py +1 -1
  396. psychopy/experiment/routines/unknown/__init__.py +2 -0
  397. psychopy/experiment/routines/{photodiodeValidator → visualValidator}/__init__.py +89 -120
  398. psychopy/experiment/routines/visualValidator/classic/visual_validator.png +0 -0
  399. psychopy/experiment/routines/visualValidator/classic/visual_validator@2x.png +0 -0
  400. psychopy/experiment/routines/visualValidator/dark/visual_validator.png +0 -0
  401. psychopy/experiment/routines/visualValidator/dark/visual_validator@2x.png +0 -0
  402. psychopy/experiment/routines/visualValidator/light/visual_validator.png +0 -0
  403. psychopy/experiment/routines/visualValidator/light/visual_validator@2x.png +0 -0
  404. psychopy/experiment/utils.py +1 -1
  405. psychopy/gui/__init__.py +1 -1
  406. psychopy/gui/qtgui.py +15 -6
  407. psychopy/gui/wxgui.py +1 -1
  408. psychopy/hardware/__init__.py +0 -1
  409. psychopy/hardware/base.py +147 -16
  410. psychopy/hardware/bbtk/__init__.py +10 -16
  411. psychopy/hardware/brainproducts.py +7 -13
  412. psychopy/hardware/button.py +21 -2
  413. psychopy/hardware/buttonbox/__init__.py +1 -1
  414. psychopy/hardware/camera/__init__.py +18 -5
  415. psychopy/hardware/cedrus.py +5 -16
  416. psychopy/hardware/crs/__init__.py +1 -1
  417. psychopy/hardware/crs/bits.py +36 -33
  418. psychopy/hardware/crs/colorcal.py +8 -11
  419. psychopy/hardware/crs/optical.py +7 -10
  420. psychopy/hardware/crs/shaders.py +18 -5
  421. psychopy/hardware/emotiv.py +1 -1
  422. psychopy/hardware/emulator.py +11 -5
  423. psychopy/hardware/exceptions.py +86 -0
  424. psychopy/hardware/forp.py +18 -23
  425. psychopy/hardware/gammasci.py +8 -3
  426. psychopy/hardware/iolab.py +8 -19
  427. psychopy/hardware/joystick/__init__.py +865 -266
  428. psychopy/hardware/joystick/_base.py +251 -0
  429. psychopy/hardware/joystick/backend_glfw.py +306 -0
  430. psychopy/hardware/joystick/backend_pyglet.py +309 -0
  431. psychopy/hardware/joystick/mappings.py +287 -0
  432. psychopy/hardware/keyboard.py +4 -2
  433. psychopy/hardware/labhackers.py +1 -1
  434. psychopy/hardware/labjacks.py +9 -13
  435. psychopy/hardware/{photodiode.py → lightsensor.py} +146 -203
  436. psychopy/hardware/listener.py +9 -8
  437. psychopy/hardware/manager.py +24 -35
  438. psychopy/hardware/microphone.py +535 -155
  439. psychopy/hardware/minolta.py +14 -4
  440. psychopy/hardware/mouse/__init__.py +1 -1
  441. psychopy/hardware/photometer/__init__.py +2 -2
  442. psychopy/hardware/pr.py +14 -4
  443. psychopy/hardware/qmix.py +18 -27
  444. psychopy/hardware/serialdevice.py +43 -12
  445. psychopy/hardware/soundsensor.py +473 -0
  446. psychopy/hardware/spatial/__init__.py +231 -0
  447. psychopy/hardware/speaker.py +298 -36
  448. psychopy/hardware/triggerbox/__init__.py +1 -1
  449. psychopy/hardware/triggerbox/base.py +1 -1
  450. psychopy/hardware/triggerbox/parallel.py +1 -1
  451. psychopy/info.py +1 -1
  452. psychopy/iohub/devices/eyetracker/__init__.py +10 -18
  453. psychopy/iohub/devices/eyetracker/calibration/procedure.py +15 -33
  454. psychopy/iohub/devices/eyetracker/hw/gazepoint/__init__.py +2 -2
  455. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/__init__.py +1 -0
  456. psychopy/iohub/devices/eyetracker/hw/gazepoint/gp3/eyetracker.py +10 -0
  457. psychopy/iohub/devices/eyetracker/hw/pupil_labs/neon/__init__.py +1 -0
  458. psychopy/iohub/devices/mouse/linux2.py +2 -1
  459. psychopy/layout.py +1 -1
  460. psychopy/liaison.py +91 -39
  461. psychopy/locale_setup.py +11 -1
  462. psychopy/localization/__init__.py +1 -1
  463. psychopy/localization/_localization.py +1 -1
  464. psychopy/localization/messages.pot +2 -2
  465. psychopy/logging.py +14 -12
  466. psychopy/microphone.py +4 -3
  467. psychopy/misc.py +1 -1
  468. psychopy/monitors/MonitorCenter.py +3 -3
  469. psychopy/monitors/__init__.py +1 -1
  470. psychopy/monitors/calibData.py +1 -1
  471. psychopy/monitors/calibTools.py +3 -2
  472. psychopy/platform_specific/__init__.py +1 -1
  473. psychopy/platform_specific/darwin.py +1 -1
  474. psychopy/platform_specific/linux.py +1 -1
  475. psychopy/platform_specific/win32.py +1 -1
  476. psychopy/plugins/__init__.py +26 -17
  477. psychopy/plugins/util.py +65 -0
  478. psychopy/preferences/Darwin.spec +11 -3
  479. psychopy/preferences/FreeBSD.spec +11 -3
  480. psychopy/preferences/Linux.spec +11 -3
  481. psychopy/preferences/Windows.spec +11 -3
  482. psychopy/preferences/__init__.py +1 -1
  483. psychopy/preferences/baseNoArch.spec +11 -3
  484. psychopy/preferences/hints.py +78 -61
  485. psychopy/preferences/preferences.py +82 -13
  486. psychopy/projects/__init__.py +1 -1
  487. psychopy/projects/pavlovia.py +29 -13
  488. psychopy/scripts/psyexpCompile.py +1 -1
  489. psychopy/session.py +81 -8
  490. psychopy/sound/__init__.py +25 -10
  491. psychopy/sound/_base.py +134 -34
  492. psychopy/sound/audioclip.py +38 -15
  493. psychopy/sound/audiodevice.py +1 -1
  494. psychopy/sound/backend_ptb.py +53 -321
  495. psychopy/sound/backend_pygame.py +7 -3
  496. psychopy/sound/backend_pyo.py +53 -22
  497. psychopy/sound/backend_pysound.py +10 -27
  498. psychopy/sound/backend_sounddevice.py +33 -21
  499. psychopy/sound/exceptions.py +1 -1
  500. psychopy/sound/microphone.py +83 -5
  501. psychopy/sound/transcribe.py +3 -3
  502. psychopy/tests/data/test_basic_run.py +1 -0
  503. psychopy/tests/data/test_sounds/default_16000.wav +0 -0
  504. psychopy/tests/data/test_sounds/default_192000.wav +0 -0
  505. psychopy/tests/data/test_sounds/default_22050.wav +0 -0
  506. psychopy/tests/data/test_sounds/default_32000.wav +0 -0
  507. psychopy/tests/data/test_sounds/default_44100.wav +0 -0
  508. psychopy/tests/data/test_sounds/default_48000.wav +0 -0
  509. psychopy/tests/data/test_sounds/default_8000.wav +0 -0
  510. psychopy/tests/data/test_sounds/default_96000.wav +0 -0
  511. psychopy/tests/test_alerts/test_alerttools.py +2 -1
  512. psychopy/tests/test_app/test_command_line.py +65 -0
  513. psychopy/tests/test_data/test_TrialHandler2.py +18 -7
  514. psychopy/tests/test_demos/test_builder_demos.py +1 -1
  515. psychopy/tests/test_experiment/needs_wx/componsTemplate.txt +5238 -4188
  516. psychopy/tests/test_experiment/needs_wx/test_components.py +1 -1
  517. psychopy/tests/test_experiment/test_components/test_RoutineSettingsComponent.py +16 -0
  518. psychopy/tests/test_experiment/test_components/test_all_components.py +1 -1
  519. psychopy/tests/test_experiment/test_components/test_base_components.py +26 -17
  520. psychopy/tests/test_experiment/test_loops.py +12 -4
  521. psychopy/tests/test_experiment/test_params.py +17 -4
  522. psychopy/tests/test_experiment/test_routines/test_PhotodiodeValidationRoutine.py +2 -2
  523. psychopy/tests/test_hardware/test_photodiode.py +66 -0
  524. psychopy/tests/test_liaison/test_Liaison.py +4 -4
  525. psychopy/tests/test_misc/test_clock.py +2 -0
  526. psychopy/tests/test_misc/test_color.py +2 -0
  527. psychopy/tests/test_misc/test_event.py +1 -0
  528. psychopy/tests/test_plugins/__init__.py +0 -0
  529. psychopy/tests/test_plugins/test_plugin_stubs.py +125 -0
  530. psychopy/tests/test_sound/test_sound.py +111 -40
  531. psychopy/tests/test_tools/test_colorspacetools.py +24 -23
  532. psychopy/tests/test_tools/test_mathtools.py +9 -9
  533. psychopy/tests/test_tools/test_viewtools.py +101 -0
  534. psychopy/tests/test_validators/__init__.py +0 -0
  535. psychopy/tests/test_validators/test_voicekeyValidator.py +95 -0
  536. psychopy/tests/test_visual/test_all_stimuli.py +2 -2
  537. psychopy/tests/test_visual/test_basevisual.py +37 -7
  538. psychopy/tests/test_visual/test_button.py +2 -2
  539. psychopy/tests/test_visual/test_circle.py +10 -2
  540. psychopy/tests/test_visual/test_dots.py +1 -1
  541. psychopy/tests/test_visual/test_form.py +13 -13
  542. psychopy/tests/test_visual/test_gamma.py +3 -3
  543. psychopy/tests/test_visual/test_image.py +2 -2
  544. psychopy/tests/test_visual/test_progress.py +2 -2
  545. psychopy/tests/test_visual/test_roi.py +8 -2
  546. psychopy/tests/test_visual/test_shape.py +2 -2
  547. psychopy/tests/test_visual/test_slider.py +2 -2
  548. psychopy/tests/test_visual/test_target.py +2 -2
  549. psychopy/tests/test_visual/test_textbox.py +6 -8
  550. psychopy/tests/test_visual/test_winScalePos.py +6 -5
  551. psychopy/tests/test_visual/test_window.py +17 -0
  552. psychopy/tests/utils.py +51 -1
  553. psychopy/tools/__init__.py +1 -1
  554. psychopy/tools/arraytools.py +2 -2
  555. psychopy/tools/attributetools.py +52 -1
  556. psychopy/tools/audiotools.py +4 -1
  557. psychopy/tools/colorspacetools.py +6 -4
  558. psychopy/tools/coordinatetools.py +1 -1
  559. psychopy/tools/fileerrortools.py +21 -9
  560. psychopy/tools/filetools.py +2 -2
  561. psychopy/tools/fontmanager.py +47 -28
  562. psychopy/tools/gltools.py +2967 -559
  563. psychopy/tools/imagetools.py +1 -1
  564. psychopy/tools/mathtools.py +997 -127
  565. psychopy/tools/monitorunittools.py +7 -1
  566. psychopy/tools/movietools.py +1 -2
  567. psychopy/tools/pkgtools.py +157 -127
  568. psychopy/tools/plottools.py +1 -1
  569. psychopy/tools/rifttools.py +1 -1
  570. psychopy/tools/stereotools.py +1 -1
  571. psychopy/tools/stimulustools.py +172 -2
  572. psychopy/tools/stringtools.py +22 -2
  573. psychopy/tools/systemtools.py +1 -1
  574. psychopy/tools/typetools.py +1 -1
  575. psychopy/tools/unittools.py +1 -1
  576. psychopy/tools/versionchooser.py +3 -1
  577. psychopy/tools/viewtools.py +54 -70
  578. psychopy/tools/wizard.py +2 -2
  579. psychopy/validation/__init__.py +6 -0
  580. psychopy/validation/audio.py +74 -0
  581. psychopy/validation/visual.py +115 -0
  582. psychopy/visual/__init__.py +1 -4
  583. psychopy/visual/aperture.py +9 -6
  584. psychopy/visual/backends/__init__.py +1 -1
  585. psychopy/visual/backends/_base.py +1 -1
  586. psychopy/visual/backends/gamma.py +1 -1
  587. psychopy/visual/backends/glfwbackend.py +8 -12
  588. psychopy/visual/backends/pygamebackend.py +1 -1
  589. psychopy/visual/backends/pygletbackend.py +32 -11
  590. psychopy/visual/basevisual.py +93 -13
  591. psychopy/visual/brush.py +1 -1
  592. psychopy/visual/bufferimage.py +90 -10
  593. psychopy/visual/button.py +1 -1
  594. psychopy/visual/circle.py +12 -20
  595. psychopy/visual/custommouse.py +1 -1
  596. psychopy/visual/dot.py +80 -14
  597. psychopy/visual/elementarray.py +84 -10
  598. psychopy/visual/filters.py +1 -1
  599. psychopy/visual/form.py +43 -28
  600. psychopy/visual/grating.py +105 -25
  601. psychopy/visual/helpers.py +4 -2
  602. psychopy/visual/image.py +98 -20
  603. psychopy/visual/line.py +15 -25
  604. psychopy/visual/movie.py +1 -1
  605. psychopy/visual/movie2.py +4 -3
  606. psychopy/visual/movie3.py +4 -3
  607. psychopy/visual/movies/__init__.py +1 -1
  608. psychopy/visual/movies/frame.py +1 -1
  609. psychopy/visual/movies/metadata.py +1 -1
  610. psychopy/visual/movies/players/__init__.py +1 -1
  611. psychopy/visual/movies/players/_base.py +1 -1
  612. psychopy/visual/movies/players/ffpyplayer_player.py +2 -3
  613. psychopy/visual/nnlvs.py +9 -6
  614. psychopy/visual/noise.py +4 -16
  615. psychopy/visual/patch.py +4 -3
  616. psychopy/visual/pie.py +3 -14
  617. psychopy/visual/polygon.py +21 -28
  618. psychopy/visual/radial.py +4 -18
  619. psychopy/visual/ratingscale.py +4 -3
  620. psychopy/visual/rect.py +16 -25
  621. psychopy/visual/rift.py +8 -2
  622. psychopy/visual/secondorder.py +4 -16
  623. psychopy/visual/shaders.py +620 -286
  624. psychopy/visual/shape.py +281 -90
  625. psychopy/visual/simpleimage.py +1 -1
  626. psychopy/visual/slider.py +78 -25
  627. psychopy/visual/stim3d.py +5 -608
  628. psychopy/visual/text.py +13 -3
  629. psychopy/visual/textbox2/textbox2.py +188 -56
  630. psychopy/visual/vlcmoviestim.py +1 -1
  631. psychopy/visual/window.py +374 -201
  632. psychopy/web.py +1 -1
  633. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/METADATA +13 -12
  634. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/RECORD +647 -624
  635. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/WHEEL +1 -1
  636. psychopy/.DS_Store +0 -0
  637. psychopy/__init__.py.orig +0 -65
  638. psychopy/app/.DS_Store +0 -0
  639. psychopy/app/Resources/.DS_Store +0 -0
  640. psychopy/app/Resources/classic/filecsv16.png +0 -0
  641. psychopy/app/Resources/classic/fileimage16.png +0 -0
  642. psychopy/app/Resources/classic/fileunknown16.png +0 -0
  643. psychopy/app/Resources/dark/filecsv16.png +0 -0
  644. psychopy/app/Resources/dark/filecsv16@2x.png +0 -0
  645. psychopy/app/Resources/dark/fileimage16.png +0 -0
  646. psychopy/app/Resources/dark/fileimage16@2x.png +0 -0
  647. psychopy/app/Resources/dark/fileunknown16.png +0 -0
  648. psychopy/app/Resources/dark/fileunknown16@2x.png +0 -0
  649. psychopy/app/Resources/fonts/OpenSans-Bold.ttf +0 -0
  650. psychopy/app/Resources/fonts/OpenSans-BoldItalic.ttf +0 -0
  651. psychopy/app/Resources/fonts/OpenSans-ExtraBold.ttf +0 -0
  652. psychopy/app/Resources/fonts/OpenSans-ExtraBoldItalic.ttf +0 -0
  653. psychopy/app/Resources/fonts/OpenSans-Italic.ttf +0 -0
  654. psychopy/app/Resources/fonts/OpenSans-Light.ttf +0 -0
  655. psychopy/app/Resources/fonts/OpenSans-LightItalic.ttf +0 -0
  656. psychopy/app/Resources/fonts/OpenSans-Regular.ttf +0 -0
  657. psychopy/app/Resources/fonts/OpenSans-SemiBold.ttf +0 -0
  658. psychopy/app/Resources/fonts/OpenSans-SemiBoldItalic.ttf +0 -0
  659. psychopy/app/Resources/light/filecsv16.png +0 -0
  660. psychopy/app/Resources/light/filecsv16@2x.png +0 -0
  661. psychopy/app/Resources/light/fileimage16.png +0 -0
  662. psychopy/app/Resources/light/fileimage16@2x.png +0 -0
  663. psychopy/app/Resources/light/fileunknown16.png +0 -0
  664. psychopy/app/Resources/light/fileunknown16@2x.png +0 -0
  665. psychopy/app/Resources/psychopySplash.png +0 -0
  666. psychopy/app/Resources/psychopySplash@2x.png +0 -0
  667. psychopy/app/builder/builder.py.orig +0 -3932
  668. psychopy/app/builder/dialogs/__init__.py.orig +0 -1679
  669. psychopy/app/builder/dialogs/paramCtrls.py.orig +0 -713
  670. psychopy/app/colorpicker/__init__.py.orig +0 -411
  671. psychopy/app/locale/ar_001/.DS_Store +0 -0
  672. psychopy/app/locale/ar_001/LC_MESSAGE/.DS_Store +0 -0
  673. psychopy/core.py.orig +0 -169
  674. psychopy/demos/builder/.DS_Store +0 -0
  675. psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +0 -330
  676. psychopy/demos/builder/Experiments/.DS_Store +0 -0
  677. psychopy/demos/builder/Experiments/dragAndDrop/archived_conditions.xlsx +0 -0
  678. psychopy/demos/builder/Experiments/dragAndDrop/draw grid stim.py +0 -61
  679. psychopy/demos/builder/Experiments/dragAndDrop/shapeMaker.psyexp +0 -91
  680. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_1.png +0 -0
  681. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_10.png +0 -0
  682. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_2.png +0 -0
  683. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_3.png +0 -0
  684. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_4.png +0 -0
  685. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_5.png +0 -0
  686. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_6.png +0 -0
  687. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_7.png +0 -0
  688. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_8.png +0 -0
  689. psychopy/demos/builder/Experiments/dragAndDrop/stimuli/grid_image_9.png +0 -0
  690. psychopy/demos/builder/Experiments/dragAndDrop/updated_conditions.xlsx +0 -0
  691. psychopy/demos/builder/Tools/.DS_Store +0 -0
  692. psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
  693. psychopy/demos/builder/Tools/gammaCalibration/data/.DS_Store +0 -0
  694. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +0 -38
  695. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +0 -3418
  696. psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
  697. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +0 -2
  698. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +0 -15
  699. psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
  700. psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +0 -562
  701. psychopy/demos/coder/.DS_Store +0 -0
  702. psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
  703. psychopy/demos/coder/iohub/.iohpid +0 -1
  704. psychopy/demos/coder/iohub/eyetracking/.iohpid +0 -1
  705. psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
  706. psychopy/demos/coder/stimuli/.DS_Store +0 -0
  707. psychopy/experiment/_experiment.py.orig +0 -1032
  708. psychopy/experiment/components/.DS_Store +0 -0
  709. psychopy/experiment/components/_base.py.orig +0 -823
  710. psychopy/experiment/components/form/.DS_Store +0 -0
  711. psychopy/experiment/components/microphone/__init__.py.orig +0 -490
  712. psychopy/experiment/components/settings/__init__.py.orig +0 -1337
  713. psychopy/experiment/components/textbox/__init__.py.orig +0 -310
  714. psychopy/experiment/components/webcam/.DS_Store +0 -0
  715. psychopy/experiment/components/webcam/light/.DS_Store +0 -0
  716. psychopy/experiment/loops.py.orig +0 -829
  717. psychopy/experiment/params.py.orig +0 -408
  718. psychopy/experiment/routine.py.orig +0 -503
  719. psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator.png +0 -0
  720. psychopy/experiment/routines/photodiodeValidator/classic/photodiode_validator@2x.png +0 -0
  721. psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator.png +0 -0
  722. psychopy/experiment/routines/photodiodeValidator/dark/photodiode_validator@2x.png +0 -0
  723. psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator.png +0 -0
  724. psychopy/experiment/routines/photodiodeValidator/light/photodiode_validator@2x.png +0 -0
  725. psychopy/hardware/.DS_Store +0 -0
  726. psychopy/hardware/brainproducts.py.orig +0 -680
  727. psychopy/hardware/iolab.py.orig +0 -238
  728. psychopy/iohub/datastore/__init__.py.orig +0 -443
  729. psychopy/iohub/datastore/util.py.orig +0 -692
  730. psychopy/iohub/devices/mouse/darwin.py.orig +0 -427
  731. psychopy/iohub/devices/mouse/linux2.py.orig +0 -198
  732. psychopy/preferences/.DS_Store +0 -0
  733. psychopy/projects/pavlovia.py.orig +0 -1295
  734. psychopy/tests/.DS_Store +0 -0
  735. psychopy/tests/data/.DS_Store +0 -0
  736. psychopy/tests/data/TestCircle_fill_local.png +0 -0
  737. psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
  738. psychopy/tests/data/aperture1_norm_local.png +0 -0
  739. psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
  740. psychopy/tests/data/beatandrcos_height_local.png +0 -0
  741. psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
  742. psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
  743. psychopy/tests/data/beatandrcos_norm_local.png +0 -0
  744. psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
  745. psychopy/tests/data/blend_add_height_local.png +0 -0
  746. psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
  747. psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
  748. psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
  749. psychopy/tests/data/blend_add_norm_local.png +0 -0
  750. psychopy/tests/data/blend_add_stencil_local.png +0 -0
  751. psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
  752. psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
  753. psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
  754. psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
  755. psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
  756. psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
  757. psychopy/tests/data/circleHex_height_local.png +0 -0
  758. psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
  759. psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
  760. psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
  761. psychopy/tests/data/circleHex_norm_local.png +0 -0
  762. psychopy/tests/data/circleHex_stencil_local.png +0 -0
  763. psychopy/tests/data/color_comparison_local.png +0 -0
  764. psychopy/tests/data/correctScript/.DS_Store +0 -0
  765. psychopy/tests/data/dots_height_local.png +0 -0
  766. psychopy/tests/data/dots_normAddBlend_local.png +0 -0
  767. psychopy/tests/data/dots_normHexbackground_local.png +0 -0
  768. psychopy/tests/data/dots_normNoShade_local.png +0 -0
  769. psychopy/tests/data/dots_norm_local.png +0 -0
  770. psychopy/tests/data/dots_stencil_local.png +0 -0
  771. psychopy/tests/data/elarray1_height_local.png +0 -0
  772. psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
  773. psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
  774. psychopy/tests/data/elarray1_norm_local.png +0 -0
  775. psychopy/tests/data/elarray1_stencil_local.png +0 -0
  776. psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
  777. psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
  778. psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
  779. psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
  780. psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
  781. psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
  782. psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
  783. psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
  784. psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
  785. psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
  786. psychopy/tests/data/gabor1_height_local.png +0 -0
  787. psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
  788. psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
  789. psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
  790. psychopy/tests/data/gabor1_norm_local.png +0 -0
  791. psychopy/tests/data/gabor1_stencil_local.png +0 -0
  792. psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
  793. psychopy/tests/data/imageAndGauss_height_local.png +0 -0
  794. psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
  795. psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
  796. psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
  797. psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
  798. psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
  799. psychopy/tests/data/movFrame1_stencil_local.png +0 -0
  800. psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
  801. psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
  802. psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
  803. psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
  804. psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
  805. psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
  806. psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
  807. psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
  808. psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
  809. psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
  810. psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
  811. psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
  812. psychopy/tests/data/numpyImage_height_local.png +0 -0
  813. psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
  814. psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
  815. psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
  816. psychopy/tests/data/numpyImage_norm_local.png +0 -0
  817. psychopy/tests/data/numpyImage_stencil_local.png +0 -0
  818. psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
  819. psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
  820. psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
  821. psychopy/tests/data/shape2_1_norm_local.png +0 -0
  822. psychopy/tests/data/shape2_1_stencil_local.png +0 -0
  823. psychopy/tests/data/text1_height_local.png +0 -0
  824. psychopy/tests/data/text1_normAddBlend_local.png +0 -0
  825. psychopy/tests/data/text1_normHexbackground_local.png +0 -0
  826. psychopy/tests/data/text1_norm_local.png +0 -0
  827. psychopy/tests/data/text1_stencil_local.png +0 -0
  828. psychopy/tests/data/wedge1_height_local.png +0 -0
  829. psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
  830. psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
  831. psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
  832. psychopy/tests/data/wedge1_norm_local.png +0 -0
  833. psychopy/tests/data/wedge1_stencil_local.png +0 -0
  834. psychopy/tests/test_app/.DS_Store +0 -0
  835. psychopy/tests/test_app/test_builder/.DS_Store +0 -0
  836. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +0 -177
  837. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
  838. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
  839. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +0 -168
  840. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
  841. psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
  842. psychopy/tests/test_data/.DS_Store +0 -0
  843. psychopy/tests/test_hardware/test_CRS_BitsSharp.py +0 -67
  844. psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +0 -68
  845. psychopy/tests/test_hardware/test_CRS_bitsShaders.py +0 -110
  846. psychopy/tests/test_visual/test_image.py.orig +0 -219
  847. psychopy/visual/basevisual.py.orig +0 -1723
  848. psychopy/visual/form.py.orig +0 -1181
  849. psychopy/visual/text.py.orig +0 -752
  850. psychopy/visual/textbox2/textbox2.py.orig +0 -1315
  851. psychopy/visual/windowwarp.py.orig +0 -463
  852. /psychopy/{app/cortex.log → alerts/alertsCatalogue/3600.yaml} +0 -0
  853. /psychopy/{app/Resources → assets}/fonts/Arvo-Bold.ttf +0 -0
  854. /psychopy/{app/Resources → assets}/fonts/Arvo-BoldItalic.ttf +0 -0
  855. /psychopy/{app/Resources → assets}/fonts/Arvo-Italic.ttf +0 -0
  856. /psychopy/{app/Resources → assets}/fonts/Arvo-Regular.ttf +0 -0
  857. /psychopy/{app/Resources → assets}/fonts/DejaVuSerif.ttf +0 -0
  858. /psychopy/{app/Resources → assets}/fonts/IndieFlower-Regular.ttf +0 -0
  859. /psychopy/{app/Resources → assets}/fonts/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  860. /psychopy/{app/Resources → assets}/fonts/JetBrainsMono-VariableFont_wght.ttf +0 -0
  861. /psychopy/demos/builder/Experiments/{GoNoGo → goNoGo}/readme.md +0 -0
  862. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/entry_points.txt +0 -0
  863. {psychopy-2024.2.5.dist-info → psychopy-2025.1.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,80 +1,829 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
- # Various math functions for working with vectors, matrices, and quaternions.
5
- #
4
+ """Various math functions for working with vectors, matrices, and quaternions.
5
+ """
6
6
 
7
7
  # Part of the PsychoPy library
8
- # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2024 Open Science Tools Ltd.
8
+ # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2025 Open Science Tools Ltd.
9
9
  # Distributed under the terms of the GNU General Public License (GPL).
10
10
 
11
- __all__ = ['normalize',
12
- 'lerp',
13
- 'slerp',
14
- 'multQuat',
15
- 'quatFromAxisAngle',
16
- 'quatToMatrix',
17
- 'scaleMatrix',
18
- 'rotationMatrix',
19
- 'transform',
20
- 'translationMatrix',
21
- 'concatenate',
22
- 'applyMatrix',
23
- 'invertQuat',
24
- 'quatToAxisAngle',
25
- 'posOriToMatrix',
26
- 'applyQuat',
27
- 'orthogonalize',
28
- 'reflect',
29
- 'cross',
30
- 'distance',
31
- 'dot',
32
- 'quatMagnitude',
33
- 'length',
34
- 'project',
35
- 'bisector',
36
- 'surfaceNormal',
37
- 'invertMatrix',
38
- 'angleTo',
39
- 'surfaceBitangent',
40
- 'surfaceTangent',
41
- 'vertexNormal',
42
- 'isOrthogonal',
43
- 'isAffine',
44
- 'perp',
45
- 'ortho3Dto2D',
46
- 'intersectRayPlane',
47
- 'matrixToQuat',
48
- 'lensCorrection',
49
- 'matrixFromEulerAngles',
50
- 'alignTo',
51
- 'quatYawPitchRoll',
52
- 'intersectRaySphere',
53
- 'intersectRayAABB',
54
- 'intersectRayOBB',
55
- 'intersectRayTriangle',
56
- 'scale',
57
- 'multMatrix',
58
- 'normalMatrix',
59
- 'fitBBox',
60
- 'computeBBoxCorners',
61
- 'zeroFix',
62
- 'accumQuat',
63
- 'fixTangentHandedness',
64
- 'articulate',
65
- 'forwardProject',
66
- 'reverseProject',
67
- 'lensCorrectionSpherical']
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
+ ]
68
90
 
69
91
 
70
92
  import numpy as np
71
93
  import functools
72
94
  import itertools
73
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))
74
745
 
75
- VEC_AXES = {'+x': (1, 0, 0), '-x': (-1, 0, 0),
76
- '+y': (0, 1, 0), '-y': (0, -1, 0),
77
- '+z': (0, 0, 1), '-z': (0, 0, -1)}
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()
78
827
 
79
828
 
80
829
  # ------------------------------------------------------------------------------
@@ -106,9 +855,10 @@ def length(v, squared=False, out=None, dtype=None):
106
855
 
107
856
  """
108
857
  if out is None:
109
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
858
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
859
+ toReturn = np.array(v, dtype=dtype)
110
860
  else:
111
- dtype = np.dtype(out.dtype).type
861
+ toReturn = out
112
862
 
113
863
  v = np.asarray(v, dtype=dtype)
114
864
 
@@ -178,14 +928,14 @@ def normalize(v, out=None, dtype=None):
178
928
 
179
929
  """
180
930
  if out is None:
181
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
931
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
182
932
  toReturn = np.array(v, dtype=dtype)
183
933
  else:
184
934
  toReturn = out
185
935
 
186
936
  v2d = np.atleast_2d(toReturn) # 2d view of array
187
937
  norm = np.linalg.norm(v2d, axis=1)
188
- norm[norm == 0.0] = np.NaN # make sure if length==0 division succeeds
938
+ norm[norm == 0.0] = np.nan # make sure if length==0 division succeeds
189
939
  v2d /= norm[:, np.newaxis]
190
940
  np.nan_to_num(v2d, copy=False) # fix NaNs
191
941
 
@@ -224,7 +974,7 @@ def orthogonalize(v, n, out=None, dtype=None):
224
974
 
225
975
  """
226
976
  if out is None:
227
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
977
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
228
978
  else:
229
979
  dtype = np.dtype(out.dtype).type
230
980
 
@@ -273,7 +1023,7 @@ def reflect(v, n, out=None, dtype=None):
273
1023
  """
274
1024
  # based off https://github.com/glfw/glfw/blob/master/deps/linmath.h
275
1025
  if out is None:
276
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1026
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
277
1027
  else:
278
1028
  dtype = np.dtype(out.dtype).type
279
1029
 
@@ -326,7 +1076,7 @@ def dot(v0, v1, out=None, dtype=None):
326
1076
 
327
1077
  """
328
1078
  if out is None:
329
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1079
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
330
1080
  else:
331
1081
  dtype = np.dtype(out.dtype).type
332
1082
 
@@ -413,7 +1163,7 @@ def cross(v0, v1, out=None, dtype=None):
413
1163
 
414
1164
  """
415
1165
  if out is None:
416
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1166
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
417
1167
  else:
418
1168
  dtype = np.dtype(out.dtype).type
419
1169
 
@@ -492,7 +1242,7 @@ def project(v0, v1, out=None, dtype=None):
492
1242
 
493
1243
  """
494
1244
  if out is None:
495
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1245
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
496
1246
  else:
497
1247
  dtype = np.dtype(out.dtype).type
498
1248
 
@@ -550,7 +1300,7 @@ def lerp(v0, v1, t, out=None, dtype=None):
550
1300
 
551
1301
  """
552
1302
  if out is None:
553
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1303
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
554
1304
  else:
555
1305
  dtype = np.dtype(out.dtype).type
556
1306
 
@@ -599,7 +1349,7 @@ def distance(v0, v1, out=None, dtype=None):
599
1349
 
600
1350
  """
601
1351
  if out is None:
602
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1352
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
603
1353
  else:
604
1354
  dtype = np.dtype(out.dtype).type
605
1355
 
@@ -659,7 +1409,7 @@ def perp(v, n, norm=True, out=None, dtype=None):
659
1409
 
660
1410
  """
661
1411
  if out is None:
662
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1412
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
663
1413
  else:
664
1414
  dtype = np.dtype(out.dtype).type
665
1415
 
@@ -709,7 +1459,7 @@ def bisector(v0, v1, norm=False, out=None, dtype=None):
709
1459
 
710
1460
  """
711
1461
  if out is None:
712
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1462
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
713
1463
  else:
714
1464
  dtype = np.dtype(out.dtype).type
715
1465
 
@@ -767,7 +1517,7 @@ def angleTo(v, point, degrees=True, out=None, dtype=None):
767
1517
 
768
1518
  """
769
1519
  if out is None:
770
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1520
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
771
1521
  else:
772
1522
  dtype = np.dtype(out.dtype).type
773
1523
 
@@ -795,13 +1545,18 @@ def angleTo(v, point, degrees=True, out=None, dtype=None):
795
1545
 
796
1546
 
797
1547
  def sortClockwise(verts):
798
- """
799
- Sort vertices clockwise from 12 O'Clock (aka vertex (0, 1)).
1548
+ """Sort vertices clockwise from 12 O'Clock (aka vertex (0, 1)).
800
1549
 
801
1550
  Parameters
802
- ==========
1551
+ ----------
803
1552
  verts : array
804
- Array of vertices to sort
1553
+ Array of vertices to sort.
1554
+
1555
+ Returns
1556
+ -------
1557
+ array
1558
+ Vertices sorted clockwise from 12 O'Clock.
1559
+
805
1560
  """
806
1561
  # Blank array of angles
807
1562
  angles = []
@@ -856,12 +1611,12 @@ def surfaceNormal(tri, norm=True, out=None, dtype=None):
856
1611
 
857
1612
  vertices = [[[-1., 0., 0.], [0., 1., 0.], [1, 0, 0]], # 2x3x3
858
1613
  [[1., 0., 0.], [0., 1., 0.], [-1, 0, 0]]]
859
- normals = np.zeros((2, 3)) # normals from two triangles triangles
1614
+ normals = np.zeros((2, 3)) # normals for two triangles
860
1615
  surfaceNormal(vertices, out=normals)
861
1616
 
862
1617
  """
863
1618
  if out is None:
864
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1619
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
865
1620
  else:
866
1621
  dtype = np.dtype(out.dtype).type
867
1622
 
@@ -945,7 +1700,7 @@ def surfaceBitangent(tri, uv, norm=True, out=None, dtype=None):
945
1700
 
946
1701
  """
947
1702
  if out is None:
948
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1703
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
949
1704
  else:
950
1705
  dtype = np.dtype(out.dtype).type
951
1706
 
@@ -1054,7 +1809,7 @@ def surfaceTangent(tri, uv, norm=True, out=None, dtype=None):
1054
1809
 
1055
1810
  """
1056
1811
  if out is None:
1057
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1812
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1058
1813
  else:
1059
1814
  dtype = np.dtype(out.dtype).type
1060
1815
 
@@ -1135,7 +1890,7 @@ def vertexNormal(faceNorms, norm=True, out=None, dtype=None):
1135
1890
 
1136
1891
  """
1137
1892
  if out is None:
1138
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1893
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1139
1894
  else:
1140
1895
  dtype = np.dtype(out.dtype).type
1141
1896
 
@@ -1188,7 +1943,7 @@ def fixTangentHandedness(tangents, normals, bitangents, out=None, dtype=None):
1188
1943
 
1189
1944
  """
1190
1945
  if out is None:
1191
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1946
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1192
1947
  else:
1193
1948
  dtype = np.dtype(out.dtype).type
1194
1949
 
@@ -1236,7 +1991,7 @@ def fitBBox(points, dtype=None):
1236
1991
  computeBBoxCorners : Convert bounding box extents to corners.
1237
1992
 
1238
1993
  """
1239
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
1994
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1240
1995
 
1241
1996
  points = np.asarray(points, dtype=dtype)
1242
1997
  extents = np.zeros((2, 3), dtype=dtype)
@@ -1344,7 +2099,7 @@ def intersectRayPlane(rayOrig, rayDir, planeOrig, planeNormal, dtype=None):
1344
2099
  pnt, dist = intersectRayPlane(rayOrigin, rayDir, planeOrigin, planeNormal)
1345
2100
 
1346
2101
  """
1347
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2102
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1348
2103
 
1349
2104
  # based off the method from GLM
1350
2105
  rayOrig = np.asarray(rayOrig, dtype=dtype)
@@ -1395,7 +2150,7 @@ def intersectRaySphere(rayOrig, rayDir, sphereOrig=(0., 0., 0.), sphereRadius=1.
1395
2150
 
1396
2151
  """
1397
2152
  # based off example from https://antongerdelan.net/opengl/raycasting.html
1398
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2153
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1399
2154
 
1400
2155
  rayOrig = np.asarray(rayOrig, dtype=dtype)
1401
2156
  rayDir = np.asarray(rayDir, dtype=dtype)
@@ -1469,7 +2224,7 @@ def intersectRayAABB(rayOrig, rayDir, boundsOffset, boundsExtents, dtype=None):
1469
2224
  # based of the example provided here:
1470
2225
  # https://www.scratchapixel.com/lessons/3d-basic-rendering/
1471
2226
  # minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection
1472
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2227
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1473
2228
 
1474
2229
  rayOrig = np.asarray(rayOrig, dtype=dtype)
1475
2230
  rayDir = np.asarray(rayDir, dtype=dtype)
@@ -1562,7 +2317,7 @@ def intersectRayOBB(rayOrig, rayDir, modelMatrix, boundsExtents, dtype=None):
1562
2317
  # based off algorithm:
1563
2318
  # https://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/
1564
2319
  # picking-with-custom-ray-obb-function/
1565
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2320
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1566
2321
 
1567
2322
  rayOrig = np.asarray(rayOrig, dtype=dtype)
1568
2323
  rayDir = np.asarray(rayDir, dtype=dtype)
@@ -1637,7 +2392,7 @@ def intersectRayTriangle(rayOrig, rayDir, tri, dtype=None):
1637
2392
 
1638
2393
  """
1639
2394
  # based off `intersectRayTriangle` from GLM (https://glm.g-truc.net)
1640
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2395
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1641
2396
 
1642
2397
  rayOrig = np.asarray(rayOrig, dtype=dtype)
1643
2398
  rayDir = np.asarray(rayDir, dtype=dtype)
@@ -1739,7 +2494,7 @@ def ortho3Dto2D(p, orig, normal, up, right=None, dtype=None):
1739
2494
  planeX, planeY = ortho3Dto2D(pnt, planeOrigin, planeNormal, planeUpAxis)
1740
2495
 
1741
2496
  """
1742
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2497
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1743
2498
 
1744
2499
  p = np.asarray(p, dtype=dtype)
1745
2500
  orig = np.asarray(orig, dtype=dtype)
@@ -1823,7 +2578,7 @@ def articulate(boneVecs, boneOris, dtype=None):
1823
2578
  wristModel.thePose.posOri = (boxPos[2, :], boxOri[2, :])
1824
2579
 
1825
2580
  """
1826
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2581
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1827
2582
 
1828
2583
  boneVecs = np.asarray(boneVecs, dtype=dtype)
1829
2584
  boneOris = np.asarray(boneOris, dtype=dtype)
@@ -1914,7 +2669,7 @@ def slerp(q0, q1, t, shortest=True, out=None, dtype=None):
1914
2669
  # https://en.wikipedia.org/wiki/Slerp
1915
2670
  #
1916
2671
  if out is None:
1917
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2672
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1918
2673
  else:
1919
2674
  dtype = np.dtype(out.dtype).type
1920
2675
 
@@ -1987,7 +2742,7 @@ def quatToAxisAngle(q, degrees=True, dtype=None):
1987
2742
  myStim.draw()
1988
2743
 
1989
2744
  """
1990
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2745
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
1991
2746
  q = normalize(q, dtype=dtype) # returns ndarray
1992
2747
  v = np.sqrt(np.sum(np.square(q[:3])))
1993
2748
 
@@ -2039,7 +2794,7 @@ def quatFromAxisAngle(axis, angle, degrees=True, dtype=None):
2039
2794
  ori = quatFromAxisAngle(axis, angle, degrees=True) # using degrees!
2040
2795
 
2041
2796
  """
2042
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2797
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2043
2798
  toReturn = np.zeros((4,), dtype=dtype)
2044
2799
 
2045
2800
  if degrees:
@@ -2097,7 +2852,7 @@ def quatYawPitchRoll(q, degrees=True, out=None, dtype=None):
2097
2852
  # based off code found here:
2098
2853
  # https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
2099
2854
  # Yields the same results as PsychXR's LibOVRPose.getYawPitchRoll method.
2100
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2855
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2101
2856
  q = np.asarray(q, dtype=dtype)
2102
2857
 
2103
2858
  toReturn = np.zeros((3,), dtype=dtype) if out is None else out
@@ -2154,7 +2909,7 @@ def quatMagnitude(q, squared=False, out=None, dtype=None):
2154
2909
 
2155
2910
  """
2156
2911
  if out is None:
2157
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2912
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2158
2913
  else:
2159
2914
  dtype = np.dtype(out.dtype).type
2160
2915
 
@@ -2217,7 +2972,7 @@ def multQuat(q0, q1, out=None, dtype=None):
2217
2972
 
2218
2973
  """
2219
2974
  if out is None:
2220
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
2975
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2221
2976
  else:
2222
2977
  dtype = np.dtype(out.dtype).type
2223
2978
 
@@ -2285,7 +3040,7 @@ def invertQuat(q, out=None, dtype=None):
2285
3040
 
2286
3041
  """
2287
3042
  if out is None:
2288
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3043
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2289
3044
  else:
2290
3045
  dtype = np.dtype(out.dtype).type
2291
3046
 
@@ -2362,7 +3117,7 @@ def applyQuat(q, points, out=None, dtype=None):
2362
3117
  """
2363
3118
  # based on 'quat_mul_vec3' implementation from linmath.h
2364
3119
  if out is None:
2365
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3120
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2366
3121
  else:
2367
3122
  dtype = np.dtype(out.dtype).type
2368
3123
 
@@ -2437,7 +3192,7 @@ def accumQuat(qlist, out=None, dtype=None):
2437
3192
 
2438
3193
  """
2439
3194
  if out is None:
2440
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3195
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2441
3196
  else:
2442
3197
  dtype = np.dtype(out.dtype).type
2443
3198
 
@@ -2501,7 +3256,7 @@ def alignTo(v, t, out=None, dtype=None):
2501
3256
  """
2502
3257
  # based off Quaternion::align from Quaternion.hpp from OpenMP
2503
3258
  if out is None:
2504
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3259
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2505
3260
  else:
2506
3261
  dtype = np.dtype(out.dtype).type
2507
3262
 
@@ -2610,7 +3365,7 @@ def matrixToQuat(m, out=None, dtype=None):
2610
3365
  # based off example `Maths - Conversion Matrix to Quaternion` from
2611
3366
  # https://www.euclideanspace.com/
2612
3367
  if out is None:
2613
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3368
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2614
3369
  else:
2615
3370
  dtype = np.dtype(out.dtype).type
2616
3371
 
@@ -2658,6 +3413,42 @@ def matrixToQuat(m, out=None, dtype=None):
2658
3413
  # Matrix Operations
2659
3414
  #
2660
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
+
2661
3452
  def quatToMatrix(q, out=None, dtype=None):
2662
3453
  """Create a 4x4 rotation matrix from a quaternion.
2663
3454
 
@@ -2704,7 +3495,7 @@ def quatToMatrix(q, out=None, dtype=None):
2704
3495
  # based off implementations from
2705
3496
  # https://github.com/glfw/glfw/blob/master/deps/linmath.h
2706
3497
  if out is None:
2707
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3498
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2708
3499
  R = np.zeros((4, 4,), dtype=dtype)
2709
3500
  else:
2710
3501
  dtype = np.dtype(out.dtype).type
@@ -2761,7 +3552,7 @@ def scaleMatrix(s, out=None, dtype=None):
2761
3552
  """
2762
3553
  # from glScale
2763
3554
  if out is None:
2764
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3555
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2765
3556
  S = np.zeros((4, 4,), dtype=dtype)
2766
3557
  else:
2767
3558
  dtype = np.dtype(out.dtype).type
@@ -2814,7 +3605,7 @@ def rotationMatrix(angle, axis=(0., 0., -1.), out=None, dtype=None):
2814
3605
 
2815
3606
  """
2816
3607
  if out is None:
2817
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3608
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2818
3609
  R = np.zeros((4, 4,), dtype=dtype)
2819
3610
  else:
2820
3611
  dtype = np.dtype(out.dtype).type
@@ -2884,7 +3675,7 @@ def translationMatrix(t, out=None, dtype=None):
2884
3675
 
2885
3676
  """
2886
3677
  if out is None:
2887
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3678
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2888
3679
  T = np.identity(4, dtype=dtype)
2889
3680
  else:
2890
3681
  dtype = np.dtype(out.dtype).type
@@ -2919,7 +3710,7 @@ def invertMatrix(m, out=None, dtype=None):
2919
3710
 
2920
3711
  """
2921
3712
  if out is None:
2922
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3713
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
2923
3714
  else:
2924
3715
  dtype = out.dtype
2925
3716
 
@@ -3026,7 +3817,7 @@ def multMatrix(matrices, reverse=False, out=None, dtype=None):
3026
3817
 
3027
3818
  """
3028
3819
  # convert matrix types
3029
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3820
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3030
3821
  matrices = np.asarray(matrices, dtype=dtype) # convert to array
3031
3822
 
3032
3823
  matrices = np.atleast_3d(matrices)
@@ -3199,7 +3990,7 @@ def matrixFromEulerAngles(rx, ry, rz, degrees=True, out=None, dtype=None):
3199
3990
  """
3200
3991
  # from https://www.j3d.org/matrix_faq/matrfaq_latest.html
3201
3992
  if out is None:
3202
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
3993
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3203
3994
  toReturn = np.zeros((4, 4,), dtype=dtype)
3204
3995
  else:
3205
3996
  dtype = np.dtype(dtype).type
@@ -3301,7 +4092,9 @@ def applyMatrix(m, points, out=None, dtype=None):
3301
4092
  Matrix with dimensions 2x2, 3x3, 3x4 or 4x4.
3302
4093
  points : array_like
3303
4094
  2D array of points/coordinates to transform. Each row should have length
3304
- appropriate for the matrix being used.
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`.
3305
4098
  out : ndarray, optional
3306
4099
  Optional output array. Must be same `shape` and `dtype` as the expected
3307
4100
  output if `out` was not specified.
@@ -3358,7 +4151,7 @@ def applyMatrix(m, points, out=None, dtype=None):
3358
4151
 
3359
4152
  """
3360
4153
  if out is None:
3361
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4154
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3362
4155
  else:
3363
4156
  dtype = np.dtype(out.dtype).type
3364
4157
 
@@ -3374,7 +4167,14 @@ def applyMatrix(m, points, out=None, dtype=None):
3374
4167
 
3375
4168
  pout, p = np.atleast_2d(toReturn, points)
3376
4169
 
3377
- if m.shape[0] == m.shape[1] == 4: # 4x4 matrix
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
3378
4178
  if pout.shape[1] == 3: # Nx3
3379
4179
  pout[:, :] = p.dot(m[:3, :3].T)
3380
4180
  pout += m[:3, 3]
@@ -3392,7 +4192,7 @@ def applyMatrix(m, points, out=None, dtype=None):
3392
4192
  raise ValueError(
3393
4193
  'Input array dimensions invalid. Should be Nx3 or Nx4 when '
3394
4194
  'input matrix is 4x4.')
3395
- elif m.shape[0] == 3 and m.shape[1] == 4: # 3x4 matrix
4195
+ elif m.shape == (3, 4): # 3x4 matrix
3396
4196
  if pout.shape[1] == 3: # Nx3
3397
4197
  pout[:, :] = p.dot(m[:3, :3].T)
3398
4198
  pout += m[:3, 3]
@@ -3400,14 +4200,14 @@ def applyMatrix(m, points, out=None, dtype=None):
3400
4200
  raise ValueError(
3401
4201
  'Input array dimensions invalid. Should be Nx3 when input '
3402
4202
  'matrix is 3x4.')
3403
- elif m.shape[0] == m.shape[1] == 3: # 3x3 matrix, e.g colors
4203
+ elif m.shape == (3, 3): # 3x3 matrix, e.g colors
3404
4204
  if pout.shape[1] == 3: # Nx3
3405
4205
  pout[:, :] = p.dot(m.T)
3406
4206
  else:
3407
4207
  raise ValueError(
3408
4208
  'Input array dimensions invalid. Should be Nx3 when '
3409
4209
  'input matrix is 3x3.')
3410
- elif m.shape[0] == m.shape[1] == pout.shape[1] == 2: # 2x2 matrix
4210
+ elif m.shape == (2, 2): # 2x2 matrix
3411
4211
  if pout.shape[1] == 2: # Nx2
3412
4212
  pout[:, :] = p.dot(m.T)
3413
4213
  else:
@@ -3449,7 +4249,7 @@ def posOriToMatrix(pos, ori, out=None, dtype=None):
3449
4249
 
3450
4250
  """
3451
4251
  if out is None:
3452
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4252
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3453
4253
  toReturn = np.zeros((4, 4,), dtype=dtype)
3454
4254
  else:
3455
4255
  dtype = np.dtype(dtype).type
@@ -3520,7 +4320,7 @@ def transform(pos, ori, points, out=None, dtype=None):
3520
4320
 
3521
4321
  """
3522
4322
  if out is None:
3523
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4323
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3524
4324
  else:
3525
4325
  dtype = np.dtype(dtype).type
3526
4326
 
@@ -3600,7 +4400,7 @@ def scale(sf, points, out=None, dtype=None):
3600
4400
 
3601
4401
  """
3602
4402
  if out is None:
3603
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4403
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3604
4404
  else:
3605
4405
  dtype = np.dtype(dtype).type
3606
4406
 
@@ -3645,7 +4445,7 @@ def normalMatrix(modelMatrix, out=None, dtype=None):
3645
4445
 
3646
4446
  """
3647
4447
  if out is None:
3648
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4448
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3649
4449
  else:
3650
4450
  dtype = np.dtype(dtype).type
3651
4451
 
@@ -3693,7 +4493,7 @@ def forwardProject(objPos, modelView, proj, viewport=None, out=None, dtype=None)
3693
4493
 
3694
4494
  """
3695
4495
  if out is None:
3696
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4496
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3697
4497
  else:
3698
4498
  dtype = np.dtype(dtype).type
3699
4499
 
@@ -3769,7 +4569,7 @@ def reverseProject(winPos, modelView, proj, viewport=None, out=None, dtype=None)
3769
4569
 
3770
4570
  """
3771
4571
  if out is None:
3772
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4572
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3773
4573
  else:
3774
4574
  dtype = np.dtype(dtype).type
3775
4575
 
@@ -3793,6 +4593,76 @@ def reverseProject(winPos, modelView, proj, viewport=None, out=None, dtype=None)
3793
4593
  return toReturn # ref to objCoord
3794
4594
 
3795
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
+
3796
4666
  # ------------------------------------------------------------------------------
3797
4667
  # Misc. Math Functions
3798
4668
  #
@@ -3892,7 +4762,7 @@ def lensCorrection(xys, coefK=(1.0,), distCenter=(0., 0.), out=None,
3892
4762
 
3893
4763
  """
3894
4764
  if out is None:
3895
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4765
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3896
4766
  else:
3897
4767
  dtype = np.dtype(dtype).type
3898
4768
 
@@ -3961,7 +4831,7 @@ def lensCorrectionSpherical(xys, coefK=1.0, aspect=1.0, out=None, dtype=None):
3961
4831
 
3962
4832
  """
3963
4833
  if out is None:
3964
- dtype = np.float64 if dtype is None else np.dtype(dtype).type
4834
+ dtype = DEFAULT_DTYPE if dtype is None else np.dtype(dtype).type
3965
4835
  else:
3966
4836
  dtype = np.dtype(dtype).type
3967
4837