vispy 0.15.0__cp313-cp313-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-313-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
@@ -0,0 +1,675 @@
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
+ ##############################################################################
8
+ # Load font into texture
9
+
10
+ from __future__ import division
11
+
12
+
13
+ import numpy as np
14
+ from copy import deepcopy
15
+ import sys
16
+
17
+ from ._sdf_gpu import SDFRendererGPU
18
+ from ._sdf_cpu import _calc_distance_field
19
+ from ...gloo import (TextureAtlas, IndexBuffer, VertexBuffer)
20
+ from ...gloo import context
21
+ from ...gloo.wrappers import _check_valid
22
+ from ...util.fonts import _load_glyph
23
+ from ..transforms import STTransform
24
+ from ...color import ColorArray
25
+ from ..visual import Visual
26
+ from ...io import load_spatial_filters
27
+
28
+
29
+ class TextureFont(object):
30
+ """Gather a set of glyphs relative to a given font name and size
31
+
32
+ This currently stores characters in a `TextureAtlas` object which uses
33
+ a 2D RGB texture to store unsigned 8-bit integer data. In the future this
34
+ could be changed to a ``GL_R8`` texture instead of RGB when OpenGL ES
35
+ 3.0+ is standard. Since VisPy tries to stay compatible with OpenGL ES 2.0
36
+ we are using an ``RGB`` texture. Using a single channel texture should
37
+ improve performance by requiring less data to be sent to the GPU and to
38
+ remote backends (jupyter notebook).
39
+
40
+ Parameters
41
+ ----------
42
+ font : dict
43
+ Dict with entries "face", "size", "bold", "italic".
44
+ renderer : instance of SDFRenderer
45
+ SDF renderer to use.
46
+
47
+ """
48
+
49
+ def __init__(self, font, renderer):
50
+ self._atlas = TextureAtlas(dtype=np.uint8)
51
+ self._atlas.wrapping = 'clamp_to_edge'
52
+ self._kernel, _ = load_spatial_filters()
53
+ self._renderer = renderer
54
+ self._font = deepcopy(font)
55
+ self._font['size'] = 256 # use high resolution point size for SDF
56
+ self._lowres_size = 64 # end at this point size for storage
57
+ assert (self._font['size'] % self._lowres_size) == 0
58
+ # spread/border at the high-res for SDF calculation; must be chosen
59
+ # relative to fragment_insert.glsl multiplication factor to ensure we
60
+ # get to zero at the edges of characters
61
+ # This is also used in SDFRendererCPU, so changing this needs to
62
+ # propagate at least 2 other places.
63
+ self._spread = 32
64
+ assert self._spread % self.ratio == 0
65
+ self._glyphs = {}
66
+
67
+ @property
68
+ def ratio(self):
69
+ """Ratio of the initial high-res to final stored low-res glyph"""
70
+ return self._font['size'] // self._lowres_size
71
+
72
+ @property
73
+ def slop(self):
74
+ """Extra space along each glyph edge due to SDF borders"""
75
+ return self._spread // self.ratio
76
+
77
+ def __getitem__(self, char):
78
+ if not (isinstance(char, str) and len(char) == 1):
79
+ raise TypeError('index must be a 1-character string')
80
+ if char not in self._glyphs:
81
+ self._load_char(char)
82
+ return self._glyphs[char]
83
+
84
+ def _load_char(self, char):
85
+ """Build and store a glyph corresponding to an individual character
86
+
87
+ Parameters
88
+ ----------
89
+ char : str
90
+ A single character to be represented.
91
+ """
92
+ assert isinstance(char, str) and len(char) == 1
93
+ assert char not in self._glyphs
94
+ # load new glyph data from font
95
+ _load_glyph(self._font, char, self._glyphs)
96
+ # put new glyph into the texture
97
+ glyph = self._glyphs[char]
98
+ bitmap = glyph['bitmap']
99
+
100
+ # convert to padded array
101
+ data = np.zeros((bitmap.shape[0] + 2*self._spread,
102
+ bitmap.shape[1] + 2*self._spread), np.uint8)
103
+ data[self._spread:-self._spread, self._spread:-self._spread] = bitmap
104
+
105
+ # Store, while scaling down to proper size
106
+ height = data.shape[0] // self.ratio
107
+ width = data.shape[1] // self.ratio
108
+ region = self._atlas.get_free_region(width + 2, height + 2)
109
+ if region is None:
110
+ raise RuntimeError('Cannot store glyph')
111
+ x, y, w, h = region
112
+ x, y, w, h = x + 1, y + 1, w - 2, h - 2
113
+
114
+ self._renderer.render_to_texture(data, self._atlas, (x, y), (w, h))
115
+ u0 = x / float(self._atlas.shape[1])
116
+ v0 = y / float(self._atlas.shape[0])
117
+ u1 = (x+w) / float(self._atlas.shape[1])
118
+ v1 = (y+h) / float(self._atlas.shape[0])
119
+ texcoords = (u0, v0, u1, v1)
120
+ glyph.update(dict(size=(w, h), texcoords=texcoords))
121
+
122
+
123
+ class FontManager(object):
124
+ """Helper to create TextureFont instances and reuse them when possible"""
125
+
126
+ # XXX: should store a font-manager on each context,
127
+ # or let TextureFont use a TextureAtlas for each context
128
+ def __init__(self, method='cpu'):
129
+ self._fonts = {}
130
+ if not isinstance(method, str) or \
131
+ method not in ('cpu', 'gpu'):
132
+ raise ValueError('method must be "cpu" or "gpu", got %s (%s)'
133
+ % (method, type(method)))
134
+ if method == 'cpu':
135
+ self._renderer = SDFRendererCPU()
136
+ else: # method == 'gpu':
137
+ self._renderer = SDFRendererGPU()
138
+
139
+ def get_font(self, face, bold=False, italic=False):
140
+ """Get a font described by face and size"""
141
+ key = '%s-%s-%s' % (face, bold, italic)
142
+ if key not in self._fonts:
143
+ font = dict(face=face, bold=bold, italic=italic)
144
+ self._fonts[key] = TextureFont(font, self._renderer)
145
+ return self._fonts[key]
146
+
147
+
148
+ ##############################################################################
149
+ # The visual
150
+
151
+ _VERTEX_SHADER = """
152
+ attribute float a_rotation; // rotation in rad
153
+ attribute vec2 a_position; // in point units
154
+ attribute vec2 a_texcoord;
155
+ attribute vec3 a_pos; // anchor position
156
+ varying vec2 v_texcoord;
157
+ varying vec4 v_color;
158
+
159
+ void main(void) {
160
+ // Eventually "rot" should be handled by SRTTransform or so...
161
+ mat4 rot = mat4(cos(a_rotation), -sin(a_rotation), 0, 0,
162
+ sin(a_rotation), cos(a_rotation), 0, 0,
163
+ 0, 0, 1, 0, 0, 0, 0, 1);
164
+ vec4 pos = $transform(vec4(a_pos, 1.0)) +
165
+ vec4($text_scale(rot * vec4(a_position, 0.0, 1.0)).xyz, 0.0);
166
+ gl_Position = pos;
167
+ v_texcoord = a_texcoord;
168
+ v_color = $color;
169
+ }
170
+ """
171
+
172
+ _FRAGMENT_SHADER = """
173
+ // Extensions for WebGL
174
+ #extension GL_OES_standard_derivatives : enable
175
+ #extension GL_OES_element_index_uint : enable
176
+ #include "misc/spatial-filters.frag"
177
+ // Adapted from glumpy with permission
178
+ const float M_SQRT1_2 = 0.707106781186547524400844362104849039;
179
+
180
+ uniform sampler2D u_font_atlas;
181
+ uniform vec2 u_font_atlas_shape;
182
+ varying vec4 v_color;
183
+ uniform float u_npix;
184
+
185
+ varying vec2 v_texcoord;
186
+ const float center = 0.5;
187
+
188
+ float contour(in float d, in float w)
189
+ {
190
+ return smoothstep(center - w, center + w, d);
191
+ }
192
+
193
+ float sample(sampler2D texture, vec2 uv, float w)
194
+ {
195
+ return contour(texture2D(texture, uv).r, w);
196
+ }
197
+
198
+ void main(void) {
199
+ vec2 uv = v_texcoord.xy;
200
+ vec4 rgb;
201
+
202
+ // Use interpolation at high font sizes
203
+ if(u_npix >= 50.0)
204
+ rgb = CatRom2D(u_font_atlas, u_font_atlas_shape, uv);
205
+ else
206
+ rgb = texture2D(u_font_atlas, uv);
207
+ float distance = rgb.r;
208
+
209
+ // GLSL's fwidth = abs(dFdx(uv)) + abs(dFdy(uv))
210
+ float width = 0.5 * fwidth(distance); // sharpens a bit
211
+
212
+ // Regular SDF
213
+ float alpha = contour(distance, width);
214
+
215
+ if (u_npix < 30.) {
216
+ // Supersample, 4 extra points
217
+ // Half of 1/sqrt2; you can play with this
218
+ float dscale = 0.5 * M_SQRT1_2;
219
+ vec2 duv = dscale * (dFdx(v_texcoord) + dFdy(v_texcoord));
220
+ vec4 box = vec4(v_texcoord-duv, v_texcoord+duv);
221
+ float asum = sample(u_font_atlas, box.xy, width)
222
+ + sample(u_font_atlas, box.zw, width)
223
+ + sample(u_font_atlas, box.xw, width)
224
+ + sample(u_font_atlas, box.zy, width);
225
+ // weighted average, with 4 extra points having 0.5 weight
226
+ // each, so 1 + 0.5*4 = 3 is the divisor
227
+ alpha = (alpha + 0.5 * asum) / 3.0;
228
+ }
229
+
230
+ if (alpha <= 0) discard;
231
+
232
+ gl_FragColor = vec4(v_color.rgb, v_color.a * alpha);
233
+ }
234
+ """
235
+
236
+
237
+ def _text_to_vbo(text, font, anchor_x, anchor_y, lowres_size):
238
+ """Convert text characters to VBO"""
239
+ # Necessary to flush commands before requesting current viewport because
240
+ # There may be a set_viewport command waiting in the queue.
241
+ # TODO: would be nicer if each canvas just remembers and manages its own
242
+ # viewport, rather than relying on the context for this.
243
+ canvas = context.get_current_canvas()
244
+ canvas.context.flush_commands()
245
+
246
+ text_vtype = np.dtype([('a_position', np.float32, 2),
247
+ ('a_texcoord', np.float32, 2)])
248
+ vertices = np.zeros(len(text) * 4, dtype=text_vtype)
249
+ prev = None
250
+ width = height = ascender = descender = 0
251
+ ratio, slop = 1. / font.ratio, font.slop
252
+ x_off = -slop
253
+ # Need to make sure we have a unicode string here (Py2.7 mis-interprets
254
+ # characters like "•" otherwise)
255
+ if sys.version[0] == '2' and isinstance(text, str):
256
+ text = text.decode('utf-8')
257
+ # Need to store the original viewport, because the font[char] will
258
+ # trigger SDF rendering, which changes our viewport
259
+ # todo: get rid of call to glGetParameter!
260
+
261
+ # Also analyse chars with large ascender and descender, otherwise the
262
+ # vertical alignment can be very inconsistent
263
+ for char in 'hy':
264
+ glyph = font[char]
265
+ y0 = glyph['offset'][1] * ratio + slop
266
+ y1 = y0 - glyph['size'][1]
267
+ ascender = max(ascender, y0 - slop)
268
+ descender = min(descender, y1 + slop)
269
+ height = max(height, glyph['size'][1] - 2*slop)
270
+
271
+ # Get/set the fonts whitespace length and line height (size of this ok?)
272
+ glyph = font[' ']
273
+ spacewidth = glyph['advance'] * ratio
274
+ lineheight = height * 1.5
275
+
276
+ # Added escape sequences characters: {unicode:offset,...}
277
+ # ord('\a') = 7
278
+ # ord('\b') = 8
279
+ # ord('\f') = 12
280
+ # ord('\n') = 10 => linebreak
281
+ # ord('\r') = 13
282
+ # ord('\t') = 9 => tab, set equal 4 whitespaces?
283
+ # ord('\v') = 11 => vertical tab, set equal 4 linebreaks?
284
+ # If text coordinate offset > 0 -> it applies to x-direction
285
+ # If text coordinate offset < 0 -> it applies to y-direction
286
+ esc_seq = {7: 0, 8: 0, 9: -4, 10: 1, 11: 4, 12: 0, 13: 0}
287
+
288
+ # Keep track of y_offset to set lines at right position
289
+ y_offset = 0
290
+
291
+ # When a line break occur, record the vertices index value
292
+ vi_marker = 0
293
+ ii_offset = 0 # Offset since certain characters won't be drawn
294
+
295
+ # The running tracker of characters vertex index
296
+ vi = 0
297
+
298
+ orig_viewport = canvas.context.get_viewport()
299
+ for ii, char in enumerate(text):
300
+ if ord(char) in esc_seq:
301
+ if esc_seq[ord(char)] < 0:
302
+ # Add offset in x-direction
303
+ x_off += abs(esc_seq[ord(char)]) * spacewidth
304
+ width += abs(esc_seq[ord(char)]) * spacewidth
305
+ elif esc_seq[ord(char)] > 0:
306
+ # Add offset in y-direction and reset things in x-direction
307
+ dx = dy = 0
308
+ if anchor_x == 'right':
309
+ dx = -width
310
+ elif anchor_x == 'center':
311
+ dx = -width / 2.
312
+ vertices['a_position'][vi_marker:vi+4] += (dx, dy)
313
+ vi_marker = vi+4
314
+ ii_offset -= 1
315
+ # Reset variables that affects x-direction positioning
316
+ x_off = -slop
317
+ width = 0
318
+ # Add offset in y-direction
319
+ y_offset += esc_seq[ord(char)] * lineheight
320
+ else:
321
+ # For ordinary characters, normal procedure
322
+ glyph = font[char]
323
+ kerning = glyph['kerning'].get(prev, 0.) * ratio
324
+ x0 = x_off + glyph['offset'][0] * ratio + kerning
325
+ y0 = glyph['offset'][1] * ratio + slop - y_offset
326
+ x1 = x0 + glyph['size'][0]
327
+ y1 = y0 - glyph['size'][1]
328
+ u0, v0, u1, v1 = glyph['texcoords']
329
+ position = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]]
330
+ texcoords = [[u0, v0], [u0, v1], [u1, v1], [u1, v0]]
331
+ vi = (ii + ii_offset) * 4
332
+ vertices['a_position'][vi:vi+4] = position
333
+ vertices['a_texcoord'][vi:vi+4] = texcoords
334
+ x_move = glyph['advance'] * ratio + kerning
335
+ x_off += x_move
336
+ ascender = max(ascender, y0 - slop)
337
+ descender = min(descender, y1 + slop)
338
+ width += x_move
339
+ height = max(height, glyph['size'][1] - 2*slop)
340
+ prev = char
341
+
342
+ if orig_viewport is not None:
343
+ canvas.context.set_viewport(*orig_viewport)
344
+
345
+ dx = dy = 0
346
+ if anchor_y == 'top':
347
+ dy = -descender
348
+ elif anchor_y in ('center', 'middle'):
349
+ dy = (-descender - ascender) / 2
350
+ elif anchor_y == 'bottom':
351
+ dy = -ascender
352
+ if anchor_x == 'right':
353
+ dx = -width
354
+ elif anchor_x == 'center':
355
+ dx = -width / 2.
356
+
357
+ # If any linebreaks occured in text, we only want to translate characters
358
+ # in the last line in text (those after the vi_marker)
359
+ vertices['a_position'][0:vi_marker] += (0, dy)
360
+ vertices['a_position'][vi_marker:] += (dx, dy)
361
+ vertices['a_position'] /= lowres_size
362
+
363
+ return vertices
364
+
365
+
366
+ class TextVisual(Visual):
367
+ """Visual that displays text
368
+
369
+ Parameters
370
+ ----------
371
+ text : str | list of str
372
+ Text to display. Can also be a list of strings.
373
+ Note: support for list of str might be removed soon
374
+ in favor of text collections.
375
+ color : instance of Color
376
+ Color to use.
377
+ bold : bool
378
+ Bold face.
379
+ italic : bool
380
+ Italic face.
381
+ face : str
382
+ Font face to use.
383
+ font_size : float
384
+ Point size to use.
385
+ pos : tuple | list of tuple
386
+ Position (x, y) or (x, y, z) of the text.
387
+ Can also be a list of tuple if `text` is a list.
388
+ rotation : float
389
+ Rotation (in degrees) of the text clockwise.
390
+ anchor_x : str
391
+ Horizontal text anchor.
392
+ anchor_y : str
393
+ Vertical text anchor.
394
+ method : str
395
+ Rendering method for text characters. Either 'cpu' (default) or
396
+ 'gpu'. The 'cpu' method should perform better on remote backends.
397
+ The 'gpu' method should produce higher quality results.
398
+ font_manager : object | None
399
+ Font manager to use (can be shared if the GLContext is shared).
400
+ depth_test : bool
401
+ Whether to apply depth testing. Default False. If False, the text
402
+ behaves like an overlay that does not get hidden behind other
403
+ visuals in the scene.
404
+ """
405
+
406
+ _shaders = {
407
+ 'vertex': _VERTEX_SHADER,
408
+ 'fragment': _FRAGMENT_SHADER,
409
+ }
410
+
411
+ def __init__(self, text=None, color='black', bold=False,
412
+ italic=False, face='OpenSans', font_size=12, pos=[0, 0, 0],
413
+ rotation=0., anchor_x='center', anchor_y='center',
414
+ method='cpu', font_manager=None, depth_test=False):
415
+ Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment'])
416
+ # Check input
417
+ valid_keys = ('top', 'center', 'middle', 'baseline', 'bottom')
418
+ _check_valid('anchor_y', anchor_y, valid_keys)
419
+ valid_keys = ('left', 'center', 'right')
420
+ _check_valid('anchor_x', anchor_x, valid_keys)
421
+ # Init font handling stuff
422
+ # _font_manager is a temporary solution to use global mananger
423
+ self._font_manager = font_manager or FontManager(method=method)
424
+ self._face = face
425
+ self._bold = bold
426
+ self._italic = italic
427
+ self._update_font()
428
+ self._vertices = None
429
+ self._color_vbo = None
430
+ self._anchors = (anchor_x, anchor_y)
431
+ # Init text properties
432
+ self.color = color
433
+ self.text = text
434
+ self.font_size = font_size
435
+ self.pos = pos
436
+ self.rotation = rotation
437
+ self._text_scale = STTransform()
438
+ self._draw_mode = 'triangles'
439
+ self.set_gl_state(blend=True, depth_test=depth_test, cull_face=False,
440
+ blend_func=('src_alpha', 'one_minus_src_alpha'))
441
+ self.freeze()
442
+
443
+ @property
444
+ def text(self):
445
+ """The text string"""
446
+ return self._text
447
+
448
+ @text.setter
449
+ def text(self, text):
450
+ if isinstance(text, list):
451
+ assert all(isinstance(t, str) for t in text)
452
+ if text is None:
453
+ text = []
454
+ self._text = text
455
+ self._vertices = None
456
+ self._pos_changed = True # need to update this as well
457
+ self._color_changed = True
458
+ self.update()
459
+
460
+ @property
461
+ def anchors(self):
462
+ return self._anchors
463
+
464
+ @anchors.setter
465
+ def anchors(self, a):
466
+ self._anchors = a
467
+ self._vertices = None
468
+ self._pos_changed = True
469
+ self.update()
470
+
471
+ @property
472
+ def font_size(self):
473
+ """The font size (in points) of the text"""
474
+ return self._font_size
475
+
476
+ @font_size.setter
477
+ def font_size(self, size):
478
+ self._font_size = max(0.0, float(size))
479
+ self.update()
480
+
481
+ @property
482
+ def color(self):
483
+ """The color of the text"""
484
+ return self._color
485
+
486
+ @color.setter
487
+ def color(self, color):
488
+ self._color = ColorArray(color)
489
+ self._color_changed = True
490
+ self.update()
491
+
492
+ @property
493
+ def rotation(self):
494
+ """The rotation of the text (clockwise, in degrees)"""
495
+ return self._rotation * 180. / np.pi
496
+
497
+ @rotation.setter
498
+ def rotation(self, rotation):
499
+ self._rotation = np.asarray(rotation) * np.pi / 180.
500
+ self._pos_changed = True
501
+ self.update()
502
+
503
+ @property
504
+ def pos(self):
505
+ """The position of the text anchor in the local coordinate frame"""
506
+ return self._pos
507
+
508
+ @pos.setter
509
+ def pos(self, pos):
510
+ pos = np.atleast_2d(pos).astype(np.float32)
511
+ if pos.shape[1] == 2:
512
+ pos = np.concatenate((pos, np.zeros((pos.shape[0], 1),
513
+ np.float32)), axis=1)
514
+ elif pos.shape[1] != 3:
515
+ raise ValueError('pos must have 2 or 3 elements')
516
+ elif pos.shape[0] == 0:
517
+ raise ValueError('at least one position must be given')
518
+ self._pos = pos
519
+ self._pos_changed = True
520
+ self.update()
521
+
522
+ def _prepare_draw(self, view):
523
+ # attributes / uniforms are not available until program is built
524
+ if len(self.text) == 0:
525
+ return False
526
+ if self._vertices is None:
527
+ text = self.text
528
+ if isinstance(text, str):
529
+ text = [text]
530
+ n_char = sum(len(t) for t in text)
531
+ # we delay creating vertices because it requires a context,
532
+ # which may or may not exist when the object is initialized
533
+ self._vertices = np.concatenate([
534
+ _text_to_vbo(t, self._font, self._anchors[0], self._anchors[1],
535
+ self._font._lowres_size) for t in text])
536
+ self._vertices = VertexBuffer(self._vertices)
537
+ idx = (np.array([0, 1, 2, 0, 2, 3], np.uint32) +
538
+ np.arange(0, 4*n_char, 4, dtype=np.uint32)[:, np.newaxis])
539
+ self._index_buffer = IndexBuffer(idx.ravel())
540
+ self.shared_program.bind(self._vertices)
541
+ # This is necessary to reset the GL drawing state after generating
542
+ # SDF textures. A better way would be to enable the state to be
543
+ # pushed/popped by the context.
544
+ self._configure_gl_state()
545
+ if self._pos_changed:
546
+ # now we promote pos to the proper shape (attribute)
547
+ text = self.text
548
+ if not isinstance(text, str):
549
+ repeats = [4 * len(t) for t in text]
550
+ text = ''.join(text)
551
+ else:
552
+ repeats = [4 * len(text)]
553
+ n_text = len(repeats)
554
+ pos = self.pos
555
+ # Rotation
556
+ _rot = self._rotation
557
+ if isinstance(_rot, (int, float)):
558
+ _rot = np.full((pos.shape[0],), self._rotation)
559
+ _rot = np.asarray(_rot)
560
+ if _rot.shape[0] < n_text:
561
+ _rep = [1] * (len(_rot) - 1) + [n_text - len(_rot) + 1]
562
+ _rot = np.repeat(_rot, _rep, axis=0)
563
+ _rot = np.repeat(_rot[:n_text], repeats, axis=0)
564
+ self.shared_program['a_rotation'] = _rot.astype(np.float32)
565
+ # Position
566
+ if pos.shape[0] < n_text:
567
+ _rep = [1] * (len(pos) - 1) + [n_text - len(pos) + 1]
568
+ pos = np.repeat(pos, _rep, axis=0)
569
+ pos = np.repeat(pos[:n_text], repeats, axis=0)
570
+ assert pos.shape[0] == self._vertices.size == len(_rot)
571
+ self.shared_program['a_pos'] = pos
572
+ self._pos_changed = False
573
+ if self._color_changed:
574
+ # now we promote color to the proper shape (varying)
575
+ text = self.text
576
+ if not isinstance(text, str):
577
+ repeats = [4 * len(t) for t in text]
578
+ text = ''.join(text)
579
+ else:
580
+ repeats = [4 * len(text)]
581
+ n_text = len(repeats)
582
+ color = self.color.rgba
583
+ if color.shape[0] < n_text:
584
+ color = np.repeat(color,
585
+ [1]*(len(color)-1) + [n_text-len(color)+1],
586
+ axis=0)
587
+ color = np.repeat(color[:n_text], repeats, axis=0)
588
+ assert color.shape[0] == self._vertices.size
589
+ self._color_vbo = VertexBuffer(color)
590
+ self.shared_program.vert['color'] = self._color_vbo
591
+ self._color_changed = False
592
+
593
+ transforms = self.transforms
594
+ n_pix = (self._font_size / 72.) * transforms.dpi # logical pix
595
+ tr = transforms.get_transform('document', 'render')
596
+ px_scale = (tr.map((1, 0)) - tr.map((0, 1)))[:2]
597
+ self._text_scale.scale = px_scale * n_pix
598
+ self.shared_program.vert['text_scale'] = self._text_scale
599
+ self.shared_program['u_npix'] = n_pix
600
+ self.shared_program['u_kernel'] = self._font._kernel
601
+ self.shared_program['u_color'] = self._color.rgba
602
+ self.shared_program['u_font_atlas'] = self._font._atlas
603
+ self.shared_program['u_font_atlas_shape'] = self._font._atlas.shape[:2]
604
+
605
+ def _prepare_transforms(self, view):
606
+ self._pos_changed = True
607
+ # Note that we access `view_program` instead of `shared_program`
608
+ # because we do not want this function assigned to other views.
609
+ tr = view.transforms.get_transform()
610
+ view.view_program.vert['transform'] = tr # .simplified()
611
+
612
+ def _compute_bounds(self, axis, view):
613
+ return self._pos[:, axis].min(), self._pos[:, axis].max()
614
+
615
+ @property
616
+ def face(self):
617
+ return self._face
618
+
619
+ @face.setter
620
+ def face(self, value):
621
+ self._face = value
622
+ self._update_font()
623
+
624
+ @property
625
+ def bold(self):
626
+ return self._bold
627
+
628
+ @bold.setter
629
+ def bold(self, value):
630
+ self._bold = value
631
+ self._update_font()
632
+
633
+ @property
634
+ def italic(self):
635
+ return self._italic
636
+
637
+ @italic.setter
638
+ def italic(self, value):
639
+ self._italic = value
640
+ self._update_font()
641
+
642
+ def _update_font(self):
643
+ self._font = self._font_manager.get_font(self._face, self._bold, self._italic)
644
+ self.update()
645
+
646
+
647
+ class SDFRendererCPU(object):
648
+ """Render SDFs using the CPU."""
649
+
650
+ # This should probably live in _sdf_cpu.pyx, but doing so makes
651
+ # debugging substantially more annoying
652
+ def render_to_texture(self, data, texture, offset, size):
653
+ sdf = (data / 255).astype(np.float32) # from ubyte -> float
654
+ h, w = sdf.shape
655
+ tex_w, tex_h = size
656
+ _calc_distance_field(sdf, w, h, 32)
657
+ # This tweaking gets us a result more similar to the GPU SDFs,
658
+ # for which the text rendering code was optimized
659
+ sdf = 2 * sdf - 1.
660
+ sdf = np.sign(sdf) * np.abs(sdf) ** 0.75 / 2. + 0.5
661
+ # Downsample using NumPy (because we can't guarantee SciPy)
662
+ xp = (np.arange(w) + 0.5) / float(w)
663
+ x = (np.arange(tex_w) + 0.5) / float(tex_w)
664
+ bitmap = np.array([np.interp(x, xp, ss) for ss in sdf])
665
+ xp = (np.arange(h) + 0.5) / float(h)
666
+ x = (np.arange(tex_h) + 0.5) / float(tex_h)
667
+ bitmap = np.array([np.interp(x, xp, ss) for ss in bitmap.T]).T
668
+ assert bitmap.shape[::-1] == size
669
+ # convert to uint8
670
+ bitmap = (bitmap * 255).astype(np.uint8)
671
+ # convert single channel to RGB by repeating
672
+ bitmap = np.tile(bitmap[..., np.newaxis],
673
+ (1, 1, 3))
674
+ texture[offset[1]:offset[1] + size[1],
675
+ offset[0]:offset[0] + size[0], :] = bitmap