vispy 0.15.0__cp312-cp312-macosx_10_13_x86_64.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 vispy might be problematic. Click here for more details.

Files changed (521) hide show
  1. vispy/__init__.py +33 -0
  2. vispy/app/__init__.py +15 -0
  3. vispy/app/_default_app.py +76 -0
  4. vispy/app/_detect_eventloop.py +148 -0
  5. vispy/app/application.py +263 -0
  6. vispy/app/backends/__init__.py +52 -0
  7. vispy/app/backends/_egl.py +264 -0
  8. vispy/app/backends/_glfw.py +513 -0
  9. vispy/app/backends/_jupyter_rfb.py +278 -0
  10. vispy/app/backends/_offscreen_util.py +121 -0
  11. vispy/app/backends/_osmesa.py +235 -0
  12. vispy/app/backends/_pyglet.py +451 -0
  13. vispy/app/backends/_pyqt4.py +36 -0
  14. vispy/app/backends/_pyqt5.py +36 -0
  15. vispy/app/backends/_pyqt6.py +40 -0
  16. vispy/app/backends/_pyside.py +37 -0
  17. vispy/app/backends/_pyside2.py +52 -0
  18. vispy/app/backends/_pyside6.py +53 -0
  19. vispy/app/backends/_qt.py +1003 -0
  20. vispy/app/backends/_sdl2.py +444 -0
  21. vispy/app/backends/_template.py +244 -0
  22. vispy/app/backends/_test.py +8 -0
  23. vispy/app/backends/_tk.py +800 -0
  24. vispy/app/backends/_wx.py +476 -0
  25. vispy/app/backends/tests/__init__.py +0 -0
  26. vispy/app/backends/tests/test_offscreen_util.py +52 -0
  27. vispy/app/backends/tests/test_rfb.py +77 -0
  28. vispy/app/base.py +294 -0
  29. vispy/app/canvas.py +828 -0
  30. vispy/app/qt.py +92 -0
  31. vispy/app/tests/__init__.py +0 -0
  32. vispy/app/tests/qt-designer.ui +58 -0
  33. vispy/app/tests/test_app.py +442 -0
  34. vispy/app/tests/test_backends.py +164 -0
  35. vispy/app/tests/test_canvas.py +122 -0
  36. vispy/app/tests/test_context.py +92 -0
  37. vispy/app/tests/test_qt.py +47 -0
  38. vispy/app/tests/test_simultaneous.py +134 -0
  39. vispy/app/timer.py +174 -0
  40. vispy/color/__init__.py +17 -0
  41. vispy/color/_color_dict.py +193 -0
  42. vispy/color/color_array.py +447 -0
  43. vispy/color/color_space.py +181 -0
  44. vispy/color/colormap.py +1213 -0
  45. vispy/color/tests/__init__.py +0 -0
  46. vispy/color/tests/test_color.py +378 -0
  47. vispy/conftest.py +12 -0
  48. vispy/ext/__init__.py +0 -0
  49. vispy/ext/cocoapy.py +1522 -0
  50. vispy/ext/cubehelix.py +138 -0
  51. vispy/ext/egl.py +375 -0
  52. vispy/ext/fontconfig.py +118 -0
  53. vispy/ext/gdi32plus.py +206 -0
  54. vispy/ext/osmesa.py +105 -0
  55. vispy/geometry/__init__.py +23 -0
  56. vispy/geometry/_triangulation_debugger.py +171 -0
  57. vispy/geometry/calculations.py +162 -0
  58. vispy/geometry/curves.py +399 -0
  59. vispy/geometry/generation.py +643 -0
  60. vispy/geometry/isocurve.py +175 -0
  61. vispy/geometry/isosurface.py +465 -0
  62. vispy/geometry/meshdata.py +700 -0
  63. vispy/geometry/normals.py +78 -0
  64. vispy/geometry/parametric.py +56 -0
  65. vispy/geometry/polygon.py +137 -0
  66. vispy/geometry/rect.py +210 -0
  67. vispy/geometry/tests/__init__.py +0 -0
  68. vispy/geometry/tests/test_calculations.py +23 -0
  69. vispy/geometry/tests/test_generation.py +56 -0
  70. vispy/geometry/tests/test_meshdata.py +106 -0
  71. vispy/geometry/tests/test_triangulation.py +594 -0
  72. vispy/geometry/torusknot.py +142 -0
  73. vispy/geometry/triangulation.py +876 -0
  74. vispy/gloo/__init__.py +56 -0
  75. vispy/gloo/buffer.py +505 -0
  76. vispy/gloo/context.py +272 -0
  77. vispy/gloo/framebuffer.py +257 -0
  78. vispy/gloo/gl/__init__.py +234 -0
  79. vispy/gloo/gl/_constants.py +332 -0
  80. vispy/gloo/gl/_es2.py +986 -0
  81. vispy/gloo/gl/_gl2.py +1365 -0
  82. vispy/gloo/gl/_proxy.py +499 -0
  83. vispy/gloo/gl/_pyopengl2.py +362 -0
  84. vispy/gloo/gl/dummy.py +24 -0
  85. vispy/gloo/gl/es2.py +62 -0
  86. vispy/gloo/gl/gl2.py +98 -0
  87. vispy/gloo/gl/glplus.py +168 -0
  88. vispy/gloo/gl/pyopengl2.py +97 -0
  89. vispy/gloo/gl/tests/__init__.py +0 -0
  90. vispy/gloo/gl/tests/test_basics.py +282 -0
  91. vispy/gloo/gl/tests/test_functionality.py +568 -0
  92. vispy/gloo/gl/tests/test_names.py +246 -0
  93. vispy/gloo/gl/tests/test_use.py +71 -0
  94. vispy/gloo/glir.py +1824 -0
  95. vispy/gloo/globject.py +101 -0
  96. vispy/gloo/preprocessor.py +67 -0
  97. vispy/gloo/program.py +543 -0
  98. vispy/gloo/tests/__init__.py +0 -0
  99. vispy/gloo/tests/test_buffer.py +558 -0
  100. vispy/gloo/tests/test_context.py +119 -0
  101. vispy/gloo/tests/test_framebuffer.py +195 -0
  102. vispy/gloo/tests/test_glir.py +307 -0
  103. vispy/gloo/tests/test_globject.py +35 -0
  104. vispy/gloo/tests/test_program.py +302 -0
  105. vispy/gloo/tests/test_texture.py +732 -0
  106. vispy/gloo/tests/test_use_gloo.py +187 -0
  107. vispy/gloo/tests/test_util.py +60 -0
  108. vispy/gloo/tests/test_wrappers.py +261 -0
  109. vispy/gloo/texture.py +1046 -0
  110. vispy/gloo/util.py +129 -0
  111. vispy/gloo/wrappers.py +762 -0
  112. vispy/glsl/__init__.py +42 -0
  113. vispy/glsl/antialias/antialias.glsl +7 -0
  114. vispy/glsl/antialias/cap-butt.glsl +31 -0
  115. vispy/glsl/antialias/cap-round.glsl +29 -0
  116. vispy/glsl/antialias/cap-square.glsl +30 -0
  117. vispy/glsl/antialias/cap-triangle-in.glsl +30 -0
  118. vispy/glsl/antialias/cap-triangle-out.glsl +30 -0
  119. vispy/glsl/antialias/cap.glsl +67 -0
  120. vispy/glsl/antialias/caps.glsl +67 -0
  121. vispy/glsl/antialias/filled.glsl +50 -0
  122. vispy/glsl/antialias/outline.glsl +40 -0
  123. vispy/glsl/antialias/stroke.glsl +43 -0
  124. vispy/glsl/arrowheads/angle.glsl +99 -0
  125. vispy/glsl/arrowheads/arrowheads.frag +60 -0
  126. vispy/glsl/arrowheads/arrowheads.glsl +12 -0
  127. vispy/glsl/arrowheads/arrowheads.vert +83 -0
  128. vispy/glsl/arrowheads/curved.glsl +48 -0
  129. vispy/glsl/arrowheads/inhibitor.glsl +26 -0
  130. vispy/glsl/arrowheads/stealth.glsl +46 -0
  131. vispy/glsl/arrowheads/triangle.glsl +97 -0
  132. vispy/glsl/arrowheads/util.glsl +13 -0
  133. vispy/glsl/arrows/angle-30.glsl +12 -0
  134. vispy/glsl/arrows/angle-60.glsl +12 -0
  135. vispy/glsl/arrows/angle-90.glsl +12 -0
  136. vispy/glsl/arrows/arrow.frag +39 -0
  137. vispy/glsl/arrows/arrow.vert +49 -0
  138. vispy/glsl/arrows/arrows.glsl +17 -0
  139. vispy/glsl/arrows/common.glsl +187 -0
  140. vispy/glsl/arrows/curved.glsl +63 -0
  141. vispy/glsl/arrows/stealth.glsl +50 -0
  142. vispy/glsl/arrows/triangle-30.glsl +12 -0
  143. vispy/glsl/arrows/triangle-60.glsl +12 -0
  144. vispy/glsl/arrows/triangle-90.glsl +12 -0
  145. vispy/glsl/arrows/util.glsl +98 -0
  146. vispy/glsl/build_spatial_filters.py +660 -0
  147. vispy/glsl/collections/agg-fast-path.frag +20 -0
  148. vispy/glsl/collections/agg-fast-path.vert +78 -0
  149. vispy/glsl/collections/agg-glyph.frag +60 -0
  150. vispy/glsl/collections/agg-glyph.vert +33 -0
  151. vispy/glsl/collections/agg-marker.frag +35 -0
  152. vispy/glsl/collections/agg-marker.vert +48 -0
  153. vispy/glsl/collections/agg-path.frag +55 -0
  154. vispy/glsl/collections/agg-path.vert +166 -0
  155. vispy/glsl/collections/agg-point.frag +21 -0
  156. vispy/glsl/collections/agg-point.vert +35 -0
  157. vispy/glsl/collections/agg-segment.frag +32 -0
  158. vispy/glsl/collections/agg-segment.vert +75 -0
  159. vispy/glsl/collections/marker.frag +38 -0
  160. vispy/glsl/collections/marker.vert +48 -0
  161. vispy/glsl/collections/raw-path.frag +15 -0
  162. vispy/glsl/collections/raw-path.vert +24 -0
  163. vispy/glsl/collections/raw-point.frag +14 -0
  164. vispy/glsl/collections/raw-point.vert +31 -0
  165. vispy/glsl/collections/raw-segment.frag +18 -0
  166. vispy/glsl/collections/raw-segment.vert +26 -0
  167. vispy/glsl/collections/raw-triangle.frag +13 -0
  168. vispy/glsl/collections/raw-triangle.vert +26 -0
  169. vispy/glsl/collections/sdf-glyph-ticks.vert +69 -0
  170. vispy/glsl/collections/sdf-glyph.frag +80 -0
  171. vispy/glsl/collections/sdf-glyph.vert +59 -0
  172. vispy/glsl/collections/tick-labels.vert +71 -0
  173. vispy/glsl/colormaps/autumn.glsl +20 -0
  174. vispy/glsl/colormaps/blues.glsl +20 -0
  175. vispy/glsl/colormaps/color-space.glsl +17 -0
  176. vispy/glsl/colormaps/colormaps.glsl +24 -0
  177. vispy/glsl/colormaps/cool.glsl +20 -0
  178. vispy/glsl/colormaps/fire.glsl +21 -0
  179. vispy/glsl/colormaps/gray.glsl +20 -0
  180. vispy/glsl/colormaps/greens.glsl +20 -0
  181. vispy/glsl/colormaps/hot.glsl +22 -0
  182. vispy/glsl/colormaps/ice.glsl +20 -0
  183. vispy/glsl/colormaps/icefire.glsl +23 -0
  184. vispy/glsl/colormaps/parse.py +40 -0
  185. vispy/glsl/colormaps/reds.glsl +20 -0
  186. vispy/glsl/colormaps/spring.glsl +20 -0
  187. vispy/glsl/colormaps/summer.glsl +20 -0
  188. vispy/glsl/colormaps/user.glsl +22 -0
  189. vispy/glsl/colormaps/util.glsl +41 -0
  190. vispy/glsl/colormaps/wheel.glsl +21 -0
  191. vispy/glsl/colormaps/winter.glsl +20 -0
  192. vispy/glsl/lines/agg.frag +320 -0
  193. vispy/glsl/lines/agg.vert +241 -0
  194. vispy/glsl/markers/arrow.glsl +12 -0
  195. vispy/glsl/markers/asterisk.glsl +16 -0
  196. vispy/glsl/markers/chevron.glsl +14 -0
  197. vispy/glsl/markers/clover.glsl +20 -0
  198. vispy/glsl/markers/club.glsl +31 -0
  199. vispy/glsl/markers/cross.glsl +17 -0
  200. vispy/glsl/markers/diamond.glsl +12 -0
  201. vispy/glsl/markers/disc.glsl +9 -0
  202. vispy/glsl/markers/ellipse.glsl +67 -0
  203. vispy/glsl/markers/hbar.glsl +9 -0
  204. vispy/glsl/markers/heart.glsl +15 -0
  205. vispy/glsl/markers/infinity.glsl +15 -0
  206. vispy/glsl/markers/marker-sdf.frag +74 -0
  207. vispy/glsl/markers/marker-sdf.vert +41 -0
  208. vispy/glsl/markers/marker.frag +36 -0
  209. vispy/glsl/markers/marker.vert +46 -0
  210. vispy/glsl/markers/markers.glsl +24 -0
  211. vispy/glsl/markers/pin.glsl +18 -0
  212. vispy/glsl/markers/ring.glsl +11 -0
  213. vispy/glsl/markers/spade.glsl +28 -0
  214. vispy/glsl/markers/square.glsl +10 -0
  215. vispy/glsl/markers/tag.glsl +11 -0
  216. vispy/glsl/markers/triangle.glsl +14 -0
  217. vispy/glsl/markers/vbar.glsl +9 -0
  218. vispy/glsl/math/circle-through-2-points.glsl +30 -0
  219. vispy/glsl/math/constants.glsl +48 -0
  220. vispy/glsl/math/double.glsl +114 -0
  221. vispy/glsl/math/functions.glsl +20 -0
  222. vispy/glsl/math/point-to-line-distance.glsl +31 -0
  223. vispy/glsl/math/point-to-line-projection.glsl +29 -0
  224. vispy/glsl/math/signed-line-distance.glsl +27 -0
  225. vispy/glsl/math/signed-segment-distance.glsl +30 -0
  226. vispy/glsl/misc/regular-grid.frag +244 -0
  227. vispy/glsl/misc/spatial-filters.frag +1407 -0
  228. vispy/glsl/misc/viewport-NDC.glsl +20 -0
  229. vispy/glsl/transforms/azimuthal-equal-area.glsl +32 -0
  230. vispy/glsl/transforms/azimuthal-equidistant.glsl +38 -0
  231. vispy/glsl/transforms/hammer.glsl +44 -0
  232. vispy/glsl/transforms/identity.glsl +6 -0
  233. vispy/glsl/transforms/identity_forward.glsl +23 -0
  234. vispy/glsl/transforms/identity_inverse.glsl +23 -0
  235. vispy/glsl/transforms/linear-scale.glsl +127 -0
  236. vispy/glsl/transforms/log-scale.glsl +126 -0
  237. vispy/glsl/transforms/mercator-transverse-forward.glsl +40 -0
  238. vispy/glsl/transforms/mercator-transverse-inverse.glsl +40 -0
  239. vispy/glsl/transforms/panzoom.glsl +10 -0
  240. vispy/glsl/transforms/polar.glsl +41 -0
  241. vispy/glsl/transforms/position.glsl +44 -0
  242. vispy/glsl/transforms/power-scale.glsl +139 -0
  243. vispy/glsl/transforms/projection.glsl +7 -0
  244. vispy/glsl/transforms/pvm.glsl +13 -0
  245. vispy/glsl/transforms/rotate.glsl +45 -0
  246. vispy/glsl/transforms/trackball.glsl +15 -0
  247. vispy/glsl/transforms/translate.glsl +35 -0
  248. vispy/glsl/transforms/transverse_mercator.glsl +38 -0
  249. vispy/glsl/transforms/viewport-clipping.glsl +14 -0
  250. vispy/glsl/transforms/viewport-transform.glsl +16 -0
  251. vispy/glsl/transforms/viewport.glsl +50 -0
  252. vispy/glsl/transforms/x.glsl +24 -0
  253. vispy/glsl/transforms/y.glsl +19 -0
  254. vispy/glsl/transforms/z.glsl +14 -0
  255. vispy/io/__init__.py +20 -0
  256. vispy/io/_data/spatial-filters.npy +0 -0
  257. vispy/io/datasets.py +94 -0
  258. vispy/io/image.py +231 -0
  259. vispy/io/mesh.py +122 -0
  260. vispy/io/stl.py +167 -0
  261. vispy/io/tests/__init__.py +0 -0
  262. vispy/io/tests/test_image.py +47 -0
  263. vispy/io/tests/test_io.py +121 -0
  264. vispy/io/wavefront.py +350 -0
  265. vispy/plot/__init__.py +36 -0
  266. vispy/plot/fig.py +58 -0
  267. vispy/plot/plotwidget.py +522 -0
  268. vispy/plot/tests/__init__.py +0 -0
  269. vispy/plot/tests/test_plot.py +46 -0
  270. vispy/scene/__init__.py +43 -0
  271. vispy/scene/cameras/__init__.py +27 -0
  272. vispy/scene/cameras/_base.py +38 -0
  273. vispy/scene/cameras/arcball.py +105 -0
  274. vispy/scene/cameras/base_camera.py +551 -0
  275. vispy/scene/cameras/fly.py +474 -0
  276. vispy/scene/cameras/magnify.py +163 -0
  277. vispy/scene/cameras/panzoom.py +311 -0
  278. vispy/scene/cameras/perspective.py +338 -0
  279. vispy/scene/cameras/tests/__init__.py +0 -0
  280. vispy/scene/cameras/tests/test_cameras.py +27 -0
  281. vispy/scene/cameras/tests/test_link.py +53 -0
  282. vispy/scene/cameras/tests/test_perspective.py +122 -0
  283. vispy/scene/cameras/turntable.py +183 -0
  284. vispy/scene/canvas.py +639 -0
  285. vispy/scene/events.py +85 -0
  286. vispy/scene/node.py +644 -0
  287. vispy/scene/subscene.py +20 -0
  288. vispy/scene/tests/__init__.py +0 -0
  289. vispy/scene/tests/test_canvas.py +119 -0
  290. vispy/scene/tests/test_node.py +142 -0
  291. vispy/scene/tests/test_visuals.py +141 -0
  292. vispy/scene/visuals.py +276 -0
  293. vispy/scene/widgets/__init__.py +18 -0
  294. vispy/scene/widgets/anchor.py +25 -0
  295. vispy/scene/widgets/axis.py +88 -0
  296. vispy/scene/widgets/colorbar.py +176 -0
  297. vispy/scene/widgets/console.py +351 -0
  298. vispy/scene/widgets/grid.py +509 -0
  299. vispy/scene/widgets/label.py +50 -0
  300. vispy/scene/widgets/tests/__init__.py +0 -0
  301. vispy/scene/widgets/tests/test_colorbar.py +47 -0
  302. vispy/scene/widgets/viewbox.py +199 -0
  303. vispy/scene/widgets/widget.py +478 -0
  304. vispy/testing/__init__.py +51 -0
  305. vispy/testing/_runners.py +448 -0
  306. vispy/testing/_testing.py +416 -0
  307. vispy/testing/image_tester.py +494 -0
  308. vispy/testing/rendered_array_tester.py +85 -0
  309. vispy/testing/tests/__init__.py +0 -0
  310. vispy/testing/tests/test_testing.py +20 -0
  311. vispy/util/__init__.py +32 -0
  312. vispy/util/bunch.py +15 -0
  313. vispy/util/check_environment.py +57 -0
  314. vispy/util/config.py +490 -0
  315. vispy/util/dpi/__init__.py +19 -0
  316. vispy/util/dpi/_linux.py +69 -0
  317. vispy/util/dpi/_quartz.py +26 -0
  318. vispy/util/dpi/_win32.py +34 -0
  319. vispy/util/dpi/tests/__init__.py +0 -0
  320. vispy/util/dpi/tests/test_dpi.py +16 -0
  321. vispy/util/eq.py +41 -0
  322. vispy/util/event.py +774 -0
  323. vispy/util/fetching.py +276 -0
  324. vispy/util/filter.py +44 -0
  325. vispy/util/fonts/__init__.py +14 -0
  326. vispy/util/fonts/_freetype.py +73 -0
  327. vispy/util/fonts/_quartz.py +192 -0
  328. vispy/util/fonts/_triage.py +36 -0
  329. vispy/util/fonts/_vispy_fonts.py +20 -0
  330. vispy/util/fonts/_win32.py +105 -0
  331. vispy/util/fonts/data/OpenSans-Bold.ttf +0 -0
  332. vispy/util/fonts/data/OpenSans-BoldItalic.ttf +0 -0
  333. vispy/util/fonts/data/OpenSans-Italic.ttf +0 -0
  334. vispy/util/fonts/data/OpenSans-Regular.ttf +0 -0
  335. vispy/util/fonts/tests/__init__.py +0 -0
  336. vispy/util/fonts/tests/test_font.py +45 -0
  337. vispy/util/fourier.py +69 -0
  338. vispy/util/frozen.py +25 -0
  339. vispy/util/gallery_scraper.py +268 -0
  340. vispy/util/keys.py +91 -0
  341. vispy/util/logs.py +358 -0
  342. vispy/util/osmesa_gl.py +17 -0
  343. vispy/util/profiler.py +135 -0
  344. vispy/util/ptime.py +16 -0
  345. vispy/util/quaternion.py +229 -0
  346. vispy/util/svg/__init__.py +18 -0
  347. vispy/util/svg/base.py +20 -0
  348. vispy/util/svg/color.py +219 -0
  349. vispy/util/svg/element.py +51 -0
  350. vispy/util/svg/geometry.py +478 -0
  351. vispy/util/svg/group.py +66 -0
  352. vispy/util/svg/length.py +81 -0
  353. vispy/util/svg/number.py +25 -0
  354. vispy/util/svg/path.py +332 -0
  355. vispy/util/svg/shapes.py +57 -0
  356. vispy/util/svg/style.py +59 -0
  357. vispy/util/svg/svg.py +40 -0
  358. vispy/util/svg/transform.py +223 -0
  359. vispy/util/svg/transformable.py +28 -0
  360. vispy/util/svg/viewport.py +73 -0
  361. vispy/util/tests/__init__.py +0 -0
  362. vispy/util/tests/test_config.py +58 -0
  363. vispy/util/tests/test_docstring_parameters.py +123 -0
  364. vispy/util/tests/test_emitter_group.py +262 -0
  365. vispy/util/tests/test_event_emitter.py +743 -0
  366. vispy/util/tests/test_fourier.py +35 -0
  367. vispy/util/tests/test_gallery_scraper.py +112 -0
  368. vispy/util/tests/test_import.py +127 -0
  369. vispy/util/tests/test_key.py +22 -0
  370. vispy/util/tests/test_logging.py +45 -0
  371. vispy/util/tests/test_run.py +14 -0
  372. vispy/util/tests/test_transforms.py +42 -0
  373. vispy/util/tests/test_vispy.py +48 -0
  374. vispy/util/transforms.py +201 -0
  375. vispy/util/wrappers.py +155 -0
  376. vispy/version.py +21 -0
  377. vispy/visuals/__init__.py +50 -0
  378. vispy/visuals/_scalable_textures.py +487 -0
  379. vispy/visuals/axis.py +678 -0
  380. vispy/visuals/border.py +208 -0
  381. vispy/visuals/box.py +79 -0
  382. vispy/visuals/collections/__init__.py +30 -0
  383. vispy/visuals/collections/agg_fast_path_collection.py +219 -0
  384. vispy/visuals/collections/agg_path_collection.py +197 -0
  385. vispy/visuals/collections/agg_point_collection.py +52 -0
  386. vispy/visuals/collections/agg_segment_collection.py +142 -0
  387. vispy/visuals/collections/array_list.py +401 -0
  388. vispy/visuals/collections/base_collection.py +482 -0
  389. vispy/visuals/collections/collection.py +253 -0
  390. vispy/visuals/collections/path_collection.py +23 -0
  391. vispy/visuals/collections/point_collection.py +19 -0
  392. vispy/visuals/collections/polygon_collection.py +25 -0
  393. vispy/visuals/collections/raw_path_collection.py +119 -0
  394. vispy/visuals/collections/raw_point_collection.py +113 -0
  395. vispy/visuals/collections/raw_polygon_collection.py +77 -0
  396. vispy/visuals/collections/raw_segment_collection.py +112 -0
  397. vispy/visuals/collections/raw_triangle_collection.py +78 -0
  398. vispy/visuals/collections/segment_collection.py +19 -0
  399. vispy/visuals/collections/triangle_collection.py +16 -0
  400. vispy/visuals/collections/util.py +168 -0
  401. vispy/visuals/colorbar.py +699 -0
  402. vispy/visuals/cube.py +41 -0
  403. vispy/visuals/ellipse.py +162 -0
  404. vispy/visuals/filters/__init__.py +10 -0
  405. vispy/visuals/filters/base_filter.py +242 -0
  406. vispy/visuals/filters/clipper.py +60 -0
  407. vispy/visuals/filters/clipping_planes.py +122 -0
  408. vispy/visuals/filters/color.py +181 -0
  409. vispy/visuals/filters/markers.py +28 -0
  410. vispy/visuals/filters/mesh.py +801 -0
  411. vispy/visuals/filters/picking.py +60 -0
  412. vispy/visuals/filters/tests/__init__.py +3 -0
  413. vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
  414. vispy/visuals/filters/tests/test_wireframe_filter.py +16 -0
  415. vispy/visuals/glsl/__init__.py +1 -0
  416. vispy/visuals/glsl/antialiasing.py +133 -0
  417. vispy/visuals/glsl/color.py +63 -0
  418. vispy/visuals/graphs/__init__.py +1 -0
  419. vispy/visuals/graphs/graph.py +240 -0
  420. vispy/visuals/graphs/layouts/__init__.py +55 -0
  421. vispy/visuals/graphs/layouts/circular.py +49 -0
  422. vispy/visuals/graphs/layouts/force_directed.py +211 -0
  423. vispy/visuals/graphs/layouts/networkx_layout.py +87 -0
  424. vispy/visuals/graphs/layouts/random.py +52 -0
  425. vispy/visuals/graphs/tests/__init__.py +1 -0
  426. vispy/visuals/graphs/tests/test_layouts.py +139 -0
  427. vispy/visuals/graphs/tests/test_networkx_layout.py +47 -0
  428. vispy/visuals/graphs/util.py +120 -0
  429. vispy/visuals/gridlines.py +161 -0
  430. vispy/visuals/gridmesh.py +98 -0
  431. vispy/visuals/histogram.py +58 -0
  432. vispy/visuals/image.py +701 -0
  433. vispy/visuals/image_complex.py +130 -0
  434. vispy/visuals/infinite_line.py +199 -0
  435. vispy/visuals/instanced_mesh.py +152 -0
  436. vispy/visuals/isocurve.py +213 -0
  437. vispy/visuals/isoline.py +241 -0
  438. vispy/visuals/isosurface.py +113 -0
  439. vispy/visuals/line/__init__.py +6 -0
  440. vispy/visuals/line/arrow.py +289 -0
  441. vispy/visuals/line/dash_atlas.py +90 -0
  442. vispy/visuals/line/line.py +545 -0
  443. vispy/visuals/line_plot.py +135 -0
  444. vispy/visuals/linear_region.py +199 -0
  445. vispy/visuals/markers.py +819 -0
  446. vispy/visuals/mesh.py +373 -0
  447. vispy/visuals/mesh_normals.py +159 -0
  448. vispy/visuals/plane.py +54 -0
  449. vispy/visuals/polygon.py +145 -0
  450. vispy/visuals/rectangle.py +196 -0
  451. vispy/visuals/regular_polygon.py +56 -0
  452. vispy/visuals/scrolling_lines.py +197 -0
  453. vispy/visuals/shaders/__init__.py +17 -0
  454. vispy/visuals/shaders/compiler.py +206 -0
  455. vispy/visuals/shaders/expression.py +99 -0
  456. vispy/visuals/shaders/function.py +788 -0
  457. vispy/visuals/shaders/multiprogram.py +145 -0
  458. vispy/visuals/shaders/parsing.py +140 -0
  459. vispy/visuals/shaders/program.py +161 -0
  460. vispy/visuals/shaders/shader_object.py +162 -0
  461. vispy/visuals/shaders/tests/__init__.py +0 -0
  462. vispy/visuals/shaders/tests/test_function.py +486 -0
  463. vispy/visuals/shaders/tests/test_multiprogram.py +78 -0
  464. vispy/visuals/shaders/tests/test_parsing.py +57 -0
  465. vispy/visuals/shaders/variable.py +272 -0
  466. vispy/visuals/spectrogram.py +169 -0
  467. vispy/visuals/sphere.py +80 -0
  468. vispy/visuals/surface_plot.py +192 -0
  469. vispy/visuals/tests/__init__.py +0 -0
  470. vispy/visuals/tests/test_arrows.py +109 -0
  471. vispy/visuals/tests/test_axis.py +120 -0
  472. vispy/visuals/tests/test_collections.py +15 -0
  473. vispy/visuals/tests/test_colorbar.py +179 -0
  474. vispy/visuals/tests/test_colormap.py +97 -0
  475. vispy/visuals/tests/test_ellipse.py +122 -0
  476. vispy/visuals/tests/test_gridlines.py +30 -0
  477. vispy/visuals/tests/test_histogram.py +24 -0
  478. vispy/visuals/tests/test_image.py +392 -0
  479. vispy/visuals/tests/test_image_complex.py +36 -0
  480. vispy/visuals/tests/test_infinite_line.py +53 -0
  481. vispy/visuals/tests/test_instanced_mesh.py +50 -0
  482. vispy/visuals/tests/test_isosurface.py +22 -0
  483. vispy/visuals/tests/test_linear_region.py +152 -0
  484. vispy/visuals/tests/test_markers.py +54 -0
  485. vispy/visuals/tests/test_mesh.py +261 -0
  486. vispy/visuals/tests/test_mesh_normals.py +218 -0
  487. vispy/visuals/tests/test_polygon.py +112 -0
  488. vispy/visuals/tests/test_rectangle.py +163 -0
  489. vispy/visuals/tests/test_regular_polygon.py +111 -0
  490. vispy/visuals/tests/test_scalable_textures.py +196 -0
  491. vispy/visuals/tests/test_sdf.py +73 -0
  492. vispy/visuals/tests/test_spectrogram.py +42 -0
  493. vispy/visuals/tests/test_surface_plot.py +57 -0
  494. vispy/visuals/tests/test_text.py +95 -0
  495. vispy/visuals/tests/test_volume.py +542 -0
  496. vispy/visuals/tests/test_windbarb.py +33 -0
  497. vispy/visuals/text/__init__.py +7 -0
  498. vispy/visuals/text/_sdf_cpu.cpython-312-darwin.so +0 -0
  499. vispy/visuals/text/_sdf_cpu.pyx +112 -0
  500. vispy/visuals/text/_sdf_gpu.py +316 -0
  501. vispy/visuals/text/text.py +675 -0
  502. vispy/visuals/transforms/__init__.py +34 -0
  503. vispy/visuals/transforms/_util.py +191 -0
  504. vispy/visuals/transforms/base_transform.py +233 -0
  505. vispy/visuals/transforms/chain.py +300 -0
  506. vispy/visuals/transforms/interactive.py +98 -0
  507. vispy/visuals/transforms/linear.py +564 -0
  508. vispy/visuals/transforms/nonlinear.py +398 -0
  509. vispy/visuals/transforms/tests/__init__.py +0 -0
  510. vispy/visuals/transforms/tests/test_transforms.py +243 -0
  511. vispy/visuals/transforms/transform_system.py +339 -0
  512. vispy/visuals/tube.py +173 -0
  513. vispy/visuals/visual.py +923 -0
  514. vispy/visuals/volume.py +1366 -0
  515. vispy/visuals/windbarb.py +291 -0
  516. vispy/visuals/xyz_axis.py +34 -0
  517. vispy-0.15.0.dist-info/METADATA +243 -0
  518. vispy-0.15.0.dist-info/RECORD +521 -0
  519. vispy-0.15.0.dist-info/WHEEL +6 -0
  520. vispy-0.15.0.dist-info/licenses/LICENSE.txt +36 -0
  521. vispy-0.15.0.dist-info/top_level.txt +1 -0
vispy/gloo/texture.py ADDED
@@ -0,0 +1,1046 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -----------------------------------------------------------------------------
3
+ # Copyright (c) Vispy Development Team. All Rights Reserved.
4
+ # Distributed under the (new) BSD License. See LICENSE.txt for more info.
5
+ # -----------------------------------------------------------------------------
6
+
7
+ import math
8
+
9
+ import numpy as np
10
+ import warnings
11
+
12
+ from .globject import GLObject
13
+ from .util import check_enum
14
+ from ..util import np_copy_if_needed
15
+
16
+
17
+ def get_dtype_limits(dtype):
18
+ if np.issubdtype(dtype, np.floating):
19
+ info = np.finfo(dtype)
20
+ else:
21
+ info = np.iinfo(dtype)
22
+ return info.min, info.max
23
+
24
+
25
+ def convert_dtype_and_clip(data, dtype, copy=False):
26
+ """
27
+ cast dtype to a new one, but first clip data to the new dtype's limits if needed
28
+ """
29
+ old_min, old_max = get_dtype_limits(data.dtype)
30
+ new_min, new_max = get_dtype_limits(dtype)
31
+ if new_max >= old_max and new_min <= old_min:
32
+ # no need to clip
33
+ return np.array(data, dtype=dtype, copy=copy or np_copy_if_needed)
34
+ else:
35
+ # to reduce copying, we clip into a pre-generated array of the right dtype
36
+ new_data = np.empty_like(data, dtype=dtype)
37
+ # allow "unsafe" casting here as we're explicitly clipping to the
38
+ # range of the new dtype - this was a default before numpy 1.25
39
+ np.clip(data, new_min, new_max, out=new_data, casting="unsafe")
40
+ return new_data
41
+
42
+
43
+ def downcast_to_32bit_if_needed(data, copy=False, dtype=None):
44
+ """Downcast to 32bit dtype if necessary."""
45
+ if dtype is None:
46
+ dtype = data.dtype
47
+ dtype = np.dtype(dtype)
48
+ if dtype.itemsize > 4:
49
+ warnings.warn(
50
+ f"GPUs can't support dtypes bigger than 32-bit, but got '{dtype}'. "
51
+ "Precision will be lost due to downcasting to 32-bit.",
52
+ stacklevel=2,
53
+ )
54
+
55
+ size = min(dtype.itemsize, 4)
56
+ kind = dtype.kind
57
+
58
+ new_dtype = np.dtype(f'{kind}{size}')
59
+ return convert_dtype_and_clip(data, new_dtype, copy=copy)
60
+
61
+
62
+ class BaseTexture(GLObject):
63
+ """
64
+ A Texture is used to represent a topological set of scalar values.
65
+
66
+ Parameters
67
+ ----------
68
+ data : ndarray | tuple | None
69
+ Texture data in the form of a numpy array (or something that
70
+ can be turned into one). A tuple with the shape of the texture
71
+ can also be given.
72
+ format : str | enum | None
73
+ The format of the texture: 'luminance', 'alpha',
74
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
75
+ is chosen automatically based on the number of channels.
76
+ When the data has one channel, 'luminance' is assumed.
77
+ resizable : bool
78
+ Indicates whether texture can be resized. Default True.
79
+ interpolation : str | None
80
+ Interpolation mode, must be one of: 'nearest', 'linear'.
81
+ Default 'nearest'.
82
+ wrapping : str | None
83
+ Wrapping mode, must be one of: 'repeat', 'clamp_to_edge',
84
+ 'mirrored_repeat'. Default 'clamp_to_edge'.
85
+ shape : tuple | None
86
+ Optional. A tuple with the shape of the texture. If ``data``
87
+ is also a tuple, it will override the value of ``shape``.
88
+ internalformat : str | None
89
+ Internal format to use.
90
+ resizeable : None
91
+ Deprecated version of `resizable`.
92
+ """
93
+
94
+ _ndim = 2
95
+
96
+ _formats = {
97
+ 1: 'luminance', # or alpha, or red
98
+ 2: 'luminance_alpha', # or rg
99
+ 3: 'rgb',
100
+ 4: 'rgba'
101
+ }
102
+
103
+ _inv_formats = {
104
+ 'luminance': 1,
105
+ 'alpha': 1,
106
+ 'red': 1,
107
+ 'luminance_alpha': 2,
108
+ 'rg': 2,
109
+ 'rgb': 3,
110
+ 'rgba': 4,
111
+ 'depth_component': 1,
112
+ }
113
+
114
+ # NOTE: non-normalized formats ending with 'i' and 'ui' are currently
115
+ # disabled as they don't work with the current VisPy implementation.
116
+ # Attempting to use them along with the additional enums defined in
117
+ # vispy/gloo/glir.py produces an invalid operation from OpenGL.
118
+ _inv_internalformats = dict([
119
+ (base + suffix, channels)
120
+ for base, channels in [('r', 1), ('rg', 2), ('rgb', 3), ('rgba', 4)]
121
+ for suffix in ['8', '16', '16f', '32f'] # , '8i', '8ui', '32i', '32ui']
122
+ ] + [
123
+ ('luminance', 1),
124
+ ('alpha', 1),
125
+ ('red', 1),
126
+ ('luminance_alpha', 2),
127
+ ('rg', 2),
128
+ ('rgb', 3),
129
+ ('rgba', 4),
130
+ ('depth_component', 1),
131
+ ])
132
+
133
+ def __init__(self, data=None, format=None, resizable=True,
134
+ interpolation=None, wrapping=None, shape=None,
135
+ internalformat=None, resizeable=None):
136
+ GLObject.__init__(self)
137
+ if resizeable is not None:
138
+ resizable = resizeable
139
+ warnings.warn(
140
+ "resizeable has been deprecated in favor of "
141
+ "resizable and will be removed next release",
142
+ DeprecationWarning,
143
+ stacklevel=2,
144
+ )
145
+
146
+ # Init shape and format
147
+ self._resizable = True # at least while we're in init
148
+ self._shape = tuple([0 for i in range(self._ndim+1)])
149
+ self._format = format
150
+ self._internalformat = internalformat
151
+
152
+ # Set texture parameters (before setting data)
153
+ self.interpolation = interpolation or 'nearest'
154
+ self.wrapping = wrapping or 'clamp_to_edge'
155
+
156
+ # Set data or shape (shape arg is for backward compat)
157
+ if isinstance(data, tuple):
158
+ shape, data = data, None
159
+ if data is not None:
160
+ if shape is not None:
161
+ raise ValueError('Texture needs data or shape, not both.')
162
+ data = np.array(data)
163
+ # So we can test the combination
164
+ self._resize(data.shape, format, internalformat)
165
+ self._set_data(data)
166
+ elif shape is not None:
167
+ self._resize(shape, format, internalformat)
168
+ else:
169
+ raise ValueError("Either data or shape must be given")
170
+
171
+ # Set resizable (at end of init)
172
+ self._resizable = bool(resizable)
173
+
174
+ def _normalize_shape(self, data_or_shape):
175
+ # Get data and shape from input
176
+ if isinstance(data_or_shape, np.ndarray):
177
+ data = data_or_shape
178
+ shape = data.shape
179
+ else:
180
+ assert isinstance(data_or_shape, tuple)
181
+ data = None
182
+ shape = data_or_shape
183
+ # Check and correct
184
+ if shape:
185
+ if len(shape) < self._ndim:
186
+ raise ValueError("Too few dimensions for texture")
187
+ elif len(shape) > self._ndim + 1:
188
+ raise ValueError("Too many dimensions for texture")
189
+ elif len(shape) == self._ndim:
190
+ shape = shape + (1,)
191
+ else: # if len(shape) == self._ndim + 1:
192
+ if shape[-1] > 4:
193
+ raise ValueError("Too many channels for texture")
194
+ # Return
195
+ return data.reshape(shape) if data is not None else shape
196
+
197
+ @property
198
+ def shape(self):
199
+ """Data shape (last dimension indicates number of color channels)"""
200
+ return self._shape
201
+
202
+ @property
203
+ def format(self):
204
+ """The texture format (color channels)."""
205
+ return self._format
206
+
207
+ @property
208
+ def internalformat(self):
209
+ """The texture internalformat."""
210
+ return self._internalformat
211
+
212
+ @property
213
+ def wrapping(self):
214
+ """Texture wrapping mode"""
215
+ value = self._wrapping
216
+ return value[0] if all([v == value[0] for v in value]) else value
217
+
218
+ @wrapping.setter
219
+ def wrapping(self, value):
220
+ # Convert
221
+ if isinstance(value, int) or isinstance(value, str):
222
+ value = (value,) * self._ndim
223
+ elif isinstance(value, (tuple, list)):
224
+ if len(value) != self._ndim:
225
+ raise ValueError('Texture wrapping needs 1 or %i values' %
226
+ self._ndim)
227
+ else:
228
+ raise ValueError('Invalid value for wrapping: %r' % value)
229
+ # Check and set
230
+ valid = 'repeat', 'clamp_to_edge', 'mirrored_repeat'
231
+ value = tuple([check_enum(value[i], 'tex wrapping', valid)
232
+ for i in range(self._ndim)])
233
+ self._wrapping = value
234
+ self._glir.command('WRAPPING', self._id, value)
235
+
236
+ @property
237
+ def interpolation(self):
238
+ """Texture interpolation for minification and magnification."""
239
+ value = self._interpolation
240
+ return value[0] if value[0] == value[1] else value
241
+
242
+ @interpolation.setter
243
+ def interpolation(self, value):
244
+ # Convert
245
+ if isinstance(value, int) or isinstance(value, str):
246
+ value = (value,) * 2
247
+ elif isinstance(value, (tuple, list)):
248
+ if len(value) != 2:
249
+ raise ValueError('Texture interpolation needs 1 or 2 values')
250
+ else:
251
+ raise ValueError('Invalid value for interpolation: %r' % value)
252
+ # Check and set
253
+ valid = 'nearest', 'linear'
254
+ value = (check_enum(value[0], 'tex interpolation', valid),
255
+ check_enum(value[1], 'tex interpolation', valid))
256
+ self._interpolation = value
257
+ self._glir.command('INTERPOLATION', self._id, *value)
258
+
259
+ def resize(self, shape, format=None, internalformat=None):
260
+ """Set the texture size and format
261
+
262
+ Parameters
263
+ ----------
264
+ shape : tuple of integers
265
+ New texture shape in zyx order. Optionally, an extra dimention
266
+ may be specified to indicate the number of color channels.
267
+ format : str | enum | None
268
+ The format of the texture: 'luminance', 'alpha',
269
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
270
+ is chosen automatically based on the number of channels.
271
+ When the data has one channel, 'luminance' is assumed.
272
+ internalformat : str | enum | None
273
+ The internal (storage) format of the texture: 'luminance',
274
+ 'alpha', 'r8', 'r16', 'r16f', 'r32f'; 'luminance_alpha',
275
+ 'rg8', 'rg16', 'rg16f', 'rg32f'; 'rgb', 'rgb8', 'rgb16',
276
+ 'rgb16f', 'rgb32f'; 'rgba', 'rgba8', 'rgba16', 'rgba16f',
277
+ 'rgba32f'. If None, the internalformat is chosen
278
+ automatically based on the number of channels. This is a
279
+ hint which may be ignored by the OpenGL implementation.
280
+ """
281
+ return self._resize(shape, format, internalformat)
282
+
283
+ def _check_format_change(self, format, num_channels):
284
+ # Determine format
285
+ if format is None:
286
+ format = self._formats[num_channels]
287
+ # Keep current format if channels match
288
+ if self._format and \
289
+ self._inv_formats[self._format] == self._inv_formats[format]:
290
+ format = self._format
291
+ else:
292
+ format = check_enum(format)
293
+
294
+ if format not in self._inv_formats:
295
+ raise ValueError('Invalid texture format: %r.' % format)
296
+ elif num_channels != self._inv_formats[format]:
297
+ raise ValueError('Format does not match with given shape. '
298
+ '(format expects %d elements, data has %d)' %
299
+ (self._inv_formats[format], num_channels))
300
+ return format
301
+
302
+ def _check_internalformat_change(self, internalformat, num_channels):
303
+ if internalformat is None:
304
+ # Keep current internalformat if channels match
305
+ if self._internalformat and \
306
+ self._inv_internalformats[self._internalformat] == num_channels:
307
+ internalformat = self._internalformat
308
+ else:
309
+ internalformat = check_enum(internalformat)
310
+
311
+ if internalformat is None:
312
+ pass
313
+ elif internalformat not in self._inv_internalformats:
314
+ raise ValueError(
315
+ 'Invalid texture internalformat: %r. Allowed formats: %r'
316
+ % (internalformat, self._inv_internalformats)
317
+ )
318
+ elif num_channels != self._inv_internalformats[internalformat]:
319
+ raise ValueError('Internalformat does not match with given shape.')
320
+ return internalformat
321
+
322
+ def _resize(self, shape, format=None, internalformat=None):
323
+ """Internal method for resize."""
324
+ shape = self._normalize_shape(shape)
325
+
326
+ # Check
327
+ if not self._resizable:
328
+ raise RuntimeError("Texture is not resizable")
329
+
330
+ format = self._check_format_change(format, shape[-1])
331
+ internalformat = self._check_internalformat_change(internalformat, shape[-1])
332
+
333
+ # Store and send GLIR command
334
+ self._shape = shape
335
+ self._format = format
336
+ self._internalformat = internalformat
337
+ self._glir.command('SIZE', self._id, self._shape, self._format,
338
+ self._internalformat)
339
+
340
+ def set_data(self, data, offset=None, copy=False):
341
+ """Set texture data
342
+
343
+ Parameters
344
+ ----------
345
+ data : ndarray
346
+ Data to be uploaded
347
+ offset: int | tuple of ints
348
+ Offset in texture where to start copying data
349
+ copy: bool
350
+ Since the operation is deferred, data may change before
351
+ data is actually uploaded to GPU memory. Asking explicitly
352
+ for a copy will prevent this behavior.
353
+
354
+ Notes
355
+ -----
356
+ This operation implicitly resizes the texture to the shape of
357
+ the data if given offset is None.
358
+ """
359
+ return self._set_data(data, offset, copy)
360
+
361
+ def _set_data(self, data, offset=None, copy=False):
362
+ """Internal method for set_data."""
363
+ # Copy if needed, check/normalize shape
364
+ data = downcast_to_32bit_if_needed(data, copy=copy)
365
+ data = self._normalize_shape(data)
366
+
367
+ # Maybe resize to purge DATA commands?
368
+ if offset is None:
369
+ self._resize(data.shape)
370
+ elif all([i == 0 for i in offset]) and data.shape == self._shape:
371
+ self._resize(data.shape)
372
+
373
+ # Convert offset to something usable
374
+ offset = offset or tuple([0 for i in range(self._ndim)])
375
+ assert len(offset) == self._ndim
376
+
377
+ # Check if data fits
378
+ for i in range(len(data.shape)-1):
379
+ if offset[i] + data.shape[i] > self._shape[i]:
380
+ raise ValueError("Data is too large")
381
+
382
+ # Send GLIR command
383
+ self._glir.command('DATA', self._id, offset, data)
384
+
385
+ def __setitem__(self, key, data):
386
+ """x.__getitem__(y) <==> x[y]"""
387
+ # Make sure key is a tuple
388
+ if isinstance(key, (int, slice)) or key == Ellipsis:
389
+ key = (key,)
390
+
391
+ # Default is to access the whole texture
392
+ shape = self._shape
393
+ slices = [slice(0, shape[i]) for i in range(len(shape))]
394
+
395
+ # Check last key/Ellipsis to decide on the order
396
+ keys = key[::+1]
397
+ dims = range(0, len(key))
398
+ if key[0] == Ellipsis:
399
+ keys = key[::-1]
400
+ dims = range(len(self._shape) - 1,
401
+ len(self._shape) - 1 - len(keys), -1)
402
+
403
+ # Find exact range for each key
404
+ for k, dim in zip(keys, dims):
405
+ size = self._shape[dim]
406
+ if isinstance(k, int):
407
+ if k < 0:
408
+ k += size
409
+ if k < 0 or k > size:
410
+ raise IndexError("Texture assignment index out of range")
411
+ start, stop = k, k + 1
412
+ slices[dim] = slice(start, stop, 1)
413
+ elif isinstance(k, slice):
414
+ start, stop, step = k.indices(size)
415
+ if step != 1:
416
+ raise IndexError("Cannot access non-contiguous data")
417
+ if stop < start:
418
+ start, stop = stop, start
419
+ slices[dim] = slice(start, stop, step)
420
+ elif k == Ellipsis:
421
+ pass
422
+ else:
423
+ raise TypeError("Texture indices must be integers")
424
+
425
+ offset = tuple([s.start for s in slices])[:self._ndim]
426
+ shape = tuple([s.stop - s.start for s in slices])
427
+ size = np.prod(shape) if len(shape) > 0 else 1
428
+
429
+ # Make sure data is an array
430
+ if not isinstance(data, np.ndarray):
431
+ data = np.array(data)
432
+ # Make sure data is big enough
433
+ if data.shape != shape:
434
+ data = np.resize(data, shape)
435
+
436
+ # Set data (deferred)
437
+ self._set_data(data=data, offset=offset, copy=False)
438
+
439
+ def __repr__(self):
440
+ return "<%s shape=%r format=%r at 0x%x>" % (
441
+ self.__class__.__name__, self._shape, self._format, id(self))
442
+
443
+
444
+ # --------------------------------------------------------- Texture1D class ---
445
+ class Texture1D(BaseTexture):
446
+ """One dimensional texture
447
+
448
+ Parameters
449
+ ----------
450
+ data : ndarray | tuple | None
451
+ Texture data in the form of a numpy array (or something that
452
+ can be turned into one). A tuple with the shape of the texture
453
+ can also be given.
454
+ format : str | enum | None
455
+ The format of the texture: 'luminance', 'alpha',
456
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
457
+ is chosen automatically based on the number of channels.
458
+ When the data has one channel, 'luminance' is assumed.
459
+ resizable : bool
460
+ Indicates whether texture can be resized. Default True.
461
+ interpolation : str | None
462
+ Interpolation mode, must be one of: 'nearest', 'linear'.
463
+ Default 'nearest'.
464
+ wrapping : str | None
465
+ Wrapping mode, must be one of: 'repeat', 'clamp_to_edge',
466
+ 'mirrored_repeat'. Default 'clamp_to_edge'.
467
+ shape : tuple | None
468
+ Optional. A tuple with the shape of the texture. If ``data``
469
+ is also a tuple, it will override the value of ``shape``.
470
+ internalformat : str | None
471
+ Internal format to use.
472
+ resizeable : None
473
+ Deprecated version of `resizable`.
474
+ """
475
+
476
+ _ndim = 1
477
+ _GLIR_TYPE = 'Texture1D'
478
+
479
+ def __init__(self, data=None, format=None, resizable=True,
480
+ interpolation=None, wrapping=None, shape=None,
481
+ internalformat=None, resizeable=None):
482
+ BaseTexture.__init__(self, data, format, resizable, interpolation,
483
+ wrapping, shape, internalformat, resizeable)
484
+
485
+ @property
486
+ def width(self):
487
+ """Texture width"""
488
+ return self._shape[0]
489
+
490
+ @property
491
+ def glsl_type(self):
492
+ """GLSL declaration strings required for a variable to hold this data."""
493
+ return 'uniform', 'sampler1D'
494
+
495
+ @property
496
+ def glsl_sampler_type(self):
497
+ """GLSL type of the sampler."""
498
+ return 'sampler1D'
499
+
500
+ @property
501
+ def glsl_sample(self):
502
+ """GLSL function that samples the texture."""
503
+ return 'texture1D'
504
+
505
+
506
+ # --------------------------------------------------------- Texture2D class ---
507
+ class Texture2D(BaseTexture):
508
+ """Two dimensional texture
509
+
510
+ Parameters
511
+ ----------
512
+ data : ndarray
513
+ Texture data shaped as W, or a tuple with the shape for
514
+ the texture (W).
515
+ format : str | enum | None
516
+ The format of the texture: 'luminance', 'alpha',
517
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
518
+ is chosen automatically based on the number of channels.
519
+ When the data has one channel, 'luminance' is assumed.
520
+ resizable : bool
521
+ Indicates whether texture can be resized. Default True.
522
+ interpolation : str
523
+ Interpolation mode, must be one of: 'nearest', 'linear'.
524
+ Default 'nearest'.
525
+ wrapping : str
526
+ Wrapping mode, must be one of: 'repeat', 'clamp_to_edge',
527
+ 'mirrored_repeat'. Default 'clamp_to_edge'.
528
+ shape : tuple
529
+ Optional. A tuple with the shape HxW. If ``data``
530
+ is also a tuple, it will override the value of ``shape``.
531
+ internalformat : str | None
532
+ Internal format to use.
533
+ resizeable : None
534
+ Deprecated version of `resizable`.
535
+ """
536
+
537
+ _ndim = 2
538
+ _GLIR_TYPE = 'Texture2D'
539
+
540
+ def __init__(self, data=None, format=None, resizable=True,
541
+ interpolation=None, wrapping=None, shape=None,
542
+ internalformat=None, resizeable=None):
543
+ BaseTexture.__init__(self, data, format, resizable, interpolation,
544
+ wrapping, shape, internalformat, resizeable)
545
+
546
+ @property
547
+ def height(self):
548
+ """Texture height"""
549
+ return self._shape[0]
550
+
551
+ @property
552
+ def width(self):
553
+ """Texture width"""
554
+ return self._shape[1]
555
+
556
+ @property
557
+ def glsl_type(self):
558
+ """GLSL declaration strings required for a variable to hold this data."""
559
+ return 'uniform', 'sampler2D'
560
+
561
+ @property
562
+ def glsl_sampler_type(self):
563
+ """GLSL type of the sampler."""
564
+ return 'sampler2D'
565
+
566
+ @property
567
+ def glsl_sample(self):
568
+ """GLSL function that samples the texture."""
569
+ return 'texture2D'
570
+
571
+
572
+ # --------------------------------------------------------- Texture3D class ---
573
+ class Texture3D(BaseTexture):
574
+ """Three dimensional texture
575
+
576
+ Parameters
577
+ ----------
578
+ data : ndarray | tuple | None
579
+ Texture data in the form of a numpy array (or something that
580
+ can be turned into one). A tuple with the shape of the texture
581
+ can also be given.
582
+ format : str | enum | None
583
+ The format of the texture: 'luminance', 'alpha',
584
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
585
+ is chosen automatically based on the number of channels.
586
+ When the data has one channel, 'luminance' is assumed.
587
+ resizable : bool
588
+ Indicates whether texture can be resized. Default True.
589
+ interpolation : str | None
590
+ Interpolation mode, must be one of: 'nearest', 'linear'.
591
+ Default 'nearest'.
592
+ wrapping : str | None
593
+ Wrapping mode, must be one of: 'repeat', 'clamp_to_edge',
594
+ 'mirrored_repeat'. Default 'clamp_to_edge'.
595
+ shape : tuple | None
596
+ Optional. A tuple with the shape of the texture. If ``data``
597
+ is also a tuple, it will override the value of ``shape``.
598
+ internalformat : str | None
599
+ Internal format to use.
600
+ resizeable : None
601
+ Deprecated version of `resizable`.
602
+ """
603
+
604
+ _ndim = 3
605
+ _GLIR_TYPE = 'Texture3D'
606
+
607
+ def __init__(self, data=None, format=None, resizable=True,
608
+ interpolation=None, wrapping=None, shape=None,
609
+ internalformat=None, resizeable=None):
610
+ BaseTexture.__init__(self, data, format, resizable, interpolation,
611
+ wrapping, shape, internalformat, resizeable)
612
+
613
+ @property
614
+ def width(self):
615
+ """Texture width"""
616
+ return self._shape[2]
617
+
618
+ @property
619
+ def height(self):
620
+ """Texture height"""
621
+ return self._shape[1]
622
+
623
+ @property
624
+ def depth(self):
625
+ """Texture depth"""
626
+ return self._shape[0]
627
+
628
+ @property
629
+ def glsl_type(self):
630
+ """GLSL declaration strings required for a variable to hold this data."""
631
+ return 'uniform', 'sampler3D'
632
+
633
+ @property
634
+ def glsl_sampler_type(self):
635
+ """GLSL type of the sampler."""
636
+ return 'sampler3D'
637
+
638
+ @property
639
+ def glsl_sample(self):
640
+ """GLSL function that samples the texture."""
641
+ return 'texture3D'
642
+
643
+
644
+ # --------------------------------------------------------- TextureCube class ---
645
+ class TextureCube(BaseTexture):
646
+ """Texture Cube
647
+
648
+ Parameters
649
+ ----------
650
+ data : ndarray | tuple | None
651
+ Texture data in the form of a numpy array (or something that
652
+ can be turned into one). A tuple with the shape of the texture
653
+ can also be given.
654
+ format : str | enum | None
655
+ The format of the texture: 'luminance', 'alpha',
656
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
657
+ is chosen automatically based on the number of channels.
658
+ When the data has one channel, 'luminance' is assumed.
659
+ resizable : bool
660
+ Indicates whether texture can be resized. Default True.
661
+ interpolation : str | None
662
+ Interpolation mode, must be one of: 'nearest', 'linear'.
663
+ Default 'nearest'.
664
+ wrapping : str | None
665
+ Wrapping mode, must be one of: 'repeat', 'clamp_to_edge',
666
+ 'mirrored_repeat'. Default 'clamp_to_edge'.
667
+ shape : tuple | None
668
+ Optional. A tuple with the shape of the texture. If ``data``
669
+ is also a tuple, it will override the value of ``shape``.
670
+ internalformat : str | None
671
+ Internal format to use.
672
+ resizeable : None
673
+ Deprecated version of `resizable`.
674
+ """
675
+
676
+ _ndim = 3
677
+ _GLIR_TYPE = 'TextureCube'
678
+
679
+ def __init__(self, data=None, format=None, resizable=True,
680
+ interpolation=None, wrapping=None, shape=None,
681
+ internalformat=None, resizeable=None):
682
+ BaseTexture.__init__(self, data, format, resizable, interpolation,
683
+ wrapping, shape, internalformat, resizeable)
684
+ if self._shape[0] != 6:
685
+ raise ValueError("Texture cube require arrays first dimension to be 6 :"
686
+ " {} was given.".format(self._shape[0]))
687
+
688
+ @property
689
+ def height(self):
690
+ """Texture height"""
691
+ return self._shape[1]
692
+
693
+ @property
694
+ def width(self):
695
+ """Texture width"""
696
+ return self._shape[2]
697
+
698
+ @property
699
+ def depth(self):
700
+ """Texture depth"""
701
+ return self._shape[0]
702
+
703
+ @property
704
+ def glsl_type(self):
705
+ """GLSL declaration strings required for a variable to hold this data."""
706
+ return 'uniform', 'samplerCube'
707
+
708
+ @property
709
+ def glsl_sampler_type(self):
710
+ """GLSL type of the sampler."""
711
+ return 'samplerCube'
712
+
713
+ @property
714
+ def glsl_sample(self):
715
+ """GLSL function that samples the texture."""
716
+ return 'textureCube'
717
+
718
+
719
+ # ------------------------------------------------- TextureEmulated3D class ---
720
+ class TextureEmulated3D(Texture2D):
721
+ """Two dimensional texture that is emulating a three dimensional texture
722
+
723
+ Parameters
724
+ ----------
725
+ data : ndarray | tuple | None
726
+ Texture data in the form of a numpy array (or something that
727
+ can be turned into one). A tuple with the shape of the texture
728
+ can also be given.
729
+ format : str | enum | None
730
+ The format of the texture: 'luminance', 'alpha',
731
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
732
+ is chosen automatically based on the number of channels.
733
+ When the data has one channel, 'luminance' is assumed.
734
+ resizable : bool
735
+ Indicates whether texture can be resized. Default True.
736
+ interpolation : str | None
737
+ Interpolation mode, must be one of: 'nearest', 'linear'.
738
+ Default 'nearest'.
739
+ wrapping : str | None
740
+ Wrapping mode, must be one of: 'repeat', 'clamp_to_edge',
741
+ 'mirrored_repeat'. Default 'clamp_to_edge'.
742
+ shape : tuple | None
743
+ Optional. A tuple with the shape of the texture. If ``data``
744
+ is also a tuple, it will override the value of ``shape``.
745
+ internalformat : str | None
746
+ Internal format to use.
747
+ resizeable : None
748
+ Deprecated version of `resizable`.
749
+ """
750
+
751
+ # TODO: does GL's nearest use floor or round?
752
+ _glsl_sample_nearest = """
753
+ vec4 sample(sampler2D tex, vec3 texcoord) {
754
+ // Don't let adjacent frames be interpolated into this one
755
+ texcoord.x = min(texcoord.x * $shape.x, $shape.x - 0.5);
756
+ texcoord.x = max(0.5, texcoord.x) / $shape.x;
757
+ texcoord.y = min(texcoord.y * $shape.y, $shape.y - 0.5);
758
+ texcoord.y = max(0.5, texcoord.y) / $shape.y;
759
+
760
+ float index = floor(texcoord.z * $shape.z);
761
+
762
+ // Do a lookup in the 2D texture
763
+ float u = (mod(index, $r) + texcoord.x) / $r;
764
+ float v = (floor(index / $r) + texcoord.y) / $c;
765
+
766
+ return texture2D(tex, vec2(u,v));
767
+ }
768
+ """
769
+
770
+ _glsl_sample_linear = """
771
+ vec4 sample(sampler2D tex, vec3 texcoord) {
772
+ // Don't let adjacent frames be interpolated into this one
773
+ texcoord.x = min(texcoord.x * $shape.x, $shape.x - 0.5);
774
+ texcoord.x = max(0.5, texcoord.x) / $shape.x;
775
+ texcoord.y = min(texcoord.y * $shape.y, $shape.y - 0.5);
776
+ texcoord.y = max(0.5, texcoord.y) / $shape.y;
777
+
778
+ float z = texcoord.z * $shape.z;
779
+ float zindex1 = floor(z);
780
+ float u1 = (mod(zindex1, $r) + texcoord.x) / $r;
781
+ float v1 = (floor(zindex1 / $r) + texcoord.y) / $c;
782
+
783
+ float zindex2 = zindex1 + 1.0;
784
+ float u2 = (mod(zindex2, $r) + texcoord.x) / $r;
785
+ float v2 = (floor(zindex2 / $r) + texcoord.y) / $c;
786
+
787
+ vec4 s1 = texture2D(tex, vec2(u1, v1));
788
+ vec4 s2 = texture2D(tex, vec2(u2, v2));
789
+
790
+ return s1 * (zindex2 - z) + s2 * (z - zindex1);
791
+ }
792
+ """
793
+
794
+ _gl_max_texture_size = 1024 # For now, we just set this manually
795
+
796
+ def __init__(self, data=None, format=None, resizable=True,
797
+ interpolation=None, wrapping=None, shape=None,
798
+ internalformat=None, resizeable=None):
799
+ from ..visuals.shaders import Function
800
+
801
+ self._set_emulated_shape(data)
802
+ Texture2D.__init__(self, self._normalize_emulated_shape(data),
803
+ format, resizable, interpolation, wrapping,
804
+ shape, internalformat, resizeable)
805
+ if self.interpolation == 'nearest':
806
+ self._glsl_sample = Function(self.__class__._glsl_sample_nearest)
807
+ else:
808
+ self._glsl_sample = Function(self.__class__._glsl_sample_linear)
809
+ self._update_variables()
810
+
811
+ def _set_emulated_shape(self, data_or_shape):
812
+ if isinstance(data_or_shape, np.ndarray):
813
+ self._emulated_shape = data_or_shape.shape
814
+ else:
815
+ assert isinstance(data_or_shape, tuple)
816
+ self._emulated_shape = tuple(data_or_shape)
817
+
818
+ depth, width = self._emulated_shape[0], self._emulated_shape[1]
819
+ self._r = TextureEmulated3D._gl_max_texture_size // width
820
+ self._c = depth // self._r
821
+ if math.fmod(depth, self._r):
822
+ self._c += 1
823
+
824
+ def _normalize_emulated_shape(self, data_or_shape):
825
+ if isinstance(data_or_shape, np.ndarray):
826
+ new_shape = self._normalize_emulated_shape(data_or_shape.shape)
827
+ new_data = np.empty(new_shape, dtype=data_or_shape.dtype)
828
+ for j in range(self._c):
829
+ for i in range(self._r):
830
+ i0, i1 = i * self.width, (i+1) * self.width
831
+ j0, j1 = j * self.height, (j+1) * self.height
832
+ k = j * self._r + i
833
+ if k >= self.depth:
834
+ break
835
+ new_data[j0:j1, i0:i1] = data_or_shape[k]
836
+
837
+ return new_data
838
+
839
+ assert isinstance(data_or_shape, tuple)
840
+ return (self._c * self.height, self._r * self.width) + \
841
+ data_or_shape[3:]
842
+
843
+ def _update_variables(self):
844
+ self._glsl_sample['shape'] = self.shape[:3][::-1]
845
+ # On Windows with Python 2.7, self._c can end up being a long
846
+ # integer because Numpy array shapes return long integers. This
847
+ # causes issues when setting the gloo variables since these are
848
+ # expected to be native ints, so we cast the integers to ints
849
+ # to avoid this.
850
+ # Newer GLSL compilers do not implicitly cast types so these integers
851
+ # must be converted to floats lastly
852
+ self._glsl_sample['c'] = float(int(self._c))
853
+ self._glsl_sample['r'] = float(int(self._r))
854
+
855
+ def set_data(self, data, offset=None, copy=False):
856
+ """Set texture data
857
+
858
+ Parameters
859
+ ----------
860
+ data : ndarray
861
+ Data to be uploaded
862
+ offset: int | tuple of ints
863
+ Offset in texture where to start copying data
864
+ copy: bool
865
+ Since the operation is deferred, data may change before
866
+ data is actually uploaded to GPU memory. Asking explicitly
867
+ for a copy will prevent this behavior.
868
+
869
+ Notes
870
+ -----
871
+ This operation implicitely resizes the texture to the shape of
872
+ the data if given offset is None.
873
+ """
874
+ self._set_emulated_shape(data)
875
+ Texture2D.set_data(self, self._normalize_emulated_shape(data),
876
+ offset, copy)
877
+ self._update_variables()
878
+
879
+ def resize(self, shape, format=None, internalformat=None):
880
+ """Set the texture size and format
881
+
882
+ Parameters
883
+ ----------
884
+ shape : tuple of integers
885
+ New texture shape in zyx order. Optionally, an extra dimention
886
+ may be specified to indicate the number of color channels.
887
+ format : str | enum | None
888
+ The format of the texture: 'luminance', 'alpha',
889
+ 'luminance_alpha', 'rgb', or 'rgba'. If not given the format
890
+ is chosen automatically based on the number of channels.
891
+ When the data has one channel, 'luminance' is assumed.
892
+ internalformat : str | enum | None
893
+ The internal (storage) format of the texture: 'luminance',
894
+ 'alpha', 'r8', 'r16', 'r16f', 'r32f'; 'luminance_alpha',
895
+ 'rg8', 'rg16', 'rg16f', 'rg32f'; 'rgb', 'rgb8', 'rgb16',
896
+ 'rgb16f', 'rgb32f'; 'rgba', 'rgba8', 'rgba16', 'rgba16f',
897
+ 'rgba32f'. If None, the internalformat is chosen
898
+ automatically based on the number of channels. This is a
899
+ hint which may be ignored by the OpenGL implementation.
900
+ """
901
+ self._set_emulated_shape(shape)
902
+ Texture2D.resize(self, self._normalize_emulated_shape(shape),
903
+ format, internalformat)
904
+ self._update_variables()
905
+
906
+ @property
907
+ def shape(self):
908
+ """Data shape (last dimension indicates number of color channels)"""
909
+ return self._emulated_shape
910
+
911
+ @property
912
+ def width(self):
913
+ """Texture width"""
914
+ return self._emulated_shape[2]
915
+
916
+ @property
917
+ def height(self):
918
+ """Texture height"""
919
+ return self._emulated_shape[1]
920
+
921
+ @property
922
+ def depth(self):
923
+ """Texture depth"""
924
+ return self._emulated_shape[0]
925
+
926
+ @property
927
+ def glsl_sample(self):
928
+ """GLSL function that samples the texture."""
929
+ return self._glsl_sample
930
+
931
+
932
+ # ------------------------------------------------------ TextureAtlas class ---
933
+ class TextureAtlas(Texture2D):
934
+ """Group multiple small data regions into a larger texture.
935
+
936
+ The algorithm is based on the article by Jukka Jylänki : "A Thousand Ways
937
+ to Pack the Bin - A Practical Approach to Two-Dimensional Rectangle Bin
938
+ Packing", February 27, 2010. More precisely, this is an implementation of
939
+ the Skyline Bottom-Left algorithm based on C++ sources provided by Jukka
940
+ Jylänki at: http://clb.demon.fi/files/RectangleBinPack/.
941
+
942
+ Parameters
943
+ ----------
944
+ shape : tuple of int
945
+ Texture shape (optional).
946
+ dtype : numpy.dtype object
947
+ Texture starting data type (default: float32)
948
+
949
+ Notes
950
+ -----
951
+ This creates a 2D texture that holds 1D float32 data.
952
+ An example of simple access:
953
+
954
+ >>> atlas = TextureAtlas()
955
+ >>> bounds = atlas.get_free_region(20, 30)
956
+ >>> atlas.set_region(bounds, np.random.rand(20, 30).T)
957
+ """
958
+
959
+ def __init__(self, shape=(1024, 1024), dtype=np.float32):
960
+ shape = np.array(shape, int)
961
+ assert shape.ndim == 1 and shape.size == 2
962
+ shape = tuple(2 ** (np.log2(shape) + 0.5).astype(int)) + (3,)
963
+ self._atlas_nodes = [(0, 0, shape[1])]
964
+ data = np.zeros(shape, dtype)
965
+ super(TextureAtlas, self).__init__(data, interpolation='linear',
966
+ wrapping='clamp_to_edge')
967
+
968
+ def get_free_region(self, width, height):
969
+ """Get a free region of given size and allocate it
970
+
971
+ Parameters
972
+ ----------
973
+ width : int
974
+ Width of region to allocate
975
+ height : int
976
+ Height of region to allocate
977
+
978
+ Returns
979
+ -------
980
+ bounds : tuple | None
981
+ A newly allocated region as (x, y, w, h) or None
982
+ (if failed).
983
+ """
984
+ best_height = best_width = np.inf
985
+ best_index = -1
986
+ for i in range(len(self._atlas_nodes)):
987
+ y = self._fit(i, width, height)
988
+ if y >= 0:
989
+ node = self._atlas_nodes[i]
990
+ if (y+height < best_height or
991
+ (y+height == best_height and node[2] < best_width)):
992
+ best_height = y+height
993
+ best_index = i
994
+ best_width = node[2]
995
+ region = node[0], y, width, height
996
+ if best_index == -1:
997
+ return None
998
+
999
+ node = region[0], region[1] + height, width
1000
+ self._atlas_nodes.insert(best_index, node)
1001
+ i = best_index+1
1002
+ while i < len(self._atlas_nodes):
1003
+ node = self._atlas_nodes[i]
1004
+ prev_node = self._atlas_nodes[i-1]
1005
+ if node[0] < prev_node[0]+prev_node[2]:
1006
+ shrink = prev_node[0]+prev_node[2] - node[0]
1007
+ x, y, w = self._atlas_nodes[i]
1008
+ self._atlas_nodes[i] = x+shrink, y, w-shrink
1009
+ if self._atlas_nodes[i][2] <= 0:
1010
+ del self._atlas_nodes[i]
1011
+ i -= 1
1012
+ else:
1013
+ break
1014
+ else:
1015
+ break
1016
+ i += 1
1017
+
1018
+ # Merge nodes
1019
+ i = 0
1020
+ while i < len(self._atlas_nodes)-1:
1021
+ node = self._atlas_nodes[i]
1022
+ next_node = self._atlas_nodes[i+1]
1023
+ if node[1] == next_node[1]:
1024
+ self._atlas_nodes[i] = node[0], node[1], node[2]+next_node[2]
1025
+ del self._atlas_nodes[i+1]
1026
+ else:
1027
+ i += 1
1028
+
1029
+ return region
1030
+
1031
+ def _fit(self, index, width, height):
1032
+ """Test if region (width, height) fit into self._atlas_nodes[index]"""
1033
+ node = self._atlas_nodes[index]
1034
+ x, y = node[0], node[1]
1035
+ width_left = width
1036
+ if x+width > self._shape[1]:
1037
+ return -1
1038
+ i = index
1039
+ while width_left > 0:
1040
+ node = self._atlas_nodes[i]
1041
+ y = max(y, node[1])
1042
+ if y+height > self._shape[0]:
1043
+ return -1
1044
+ width_left -= node[2]
1045
+ i += 1
1046
+ return y