vispy 0.14.0__cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.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.
- vispy/__init__.py +33 -0
- vispy/app/__init__.py +15 -0
- vispy/app/_default_app.py +76 -0
- vispy/app/_detect_eventloop.py +148 -0
- vispy/app/application.py +263 -0
- vispy/app/backends/__init__.py +52 -0
- vispy/app/backends/_egl.py +264 -0
- vispy/app/backends/_glfw.py +513 -0
- vispy/app/backends/_jupyter_rfb.py +278 -0
- vispy/app/backends/_offscreen_util.py +121 -0
- vispy/app/backends/_osmesa.py +235 -0
- vispy/app/backends/_pyglet.py +451 -0
- vispy/app/backends/_pyqt4.py +36 -0
- vispy/app/backends/_pyqt5.py +36 -0
- vispy/app/backends/_pyqt6.py +40 -0
- vispy/app/backends/_pyside.py +37 -0
- vispy/app/backends/_pyside2.py +52 -0
- vispy/app/backends/_pyside6.py +53 -0
- vispy/app/backends/_qt.py +968 -0
- vispy/app/backends/_sdl2.py +444 -0
- vispy/app/backends/_template.py +244 -0
- vispy/app/backends/_test.py +8 -0
- vispy/app/backends/_tk.py +800 -0
- vispy/app/backends/_wx.py +476 -0
- vispy/app/backends/tests/__init__.py +0 -0
- vispy/app/backends/tests/test_offscreen_util.py +52 -0
- vispy/app/backends/tests/test_rfb.py +77 -0
- vispy/app/base.py +294 -0
- vispy/app/canvas.py +828 -0
- vispy/app/qt.py +92 -0
- vispy/app/tests/__init__.py +0 -0
- vispy/app/tests/qt-designer.ui +58 -0
- vispy/app/tests/test_app.py +442 -0
- vispy/app/tests/test_backends.py +164 -0
- vispy/app/tests/test_canvas.py +122 -0
- vispy/app/tests/test_context.py +92 -0
- vispy/app/tests/test_qt.py +47 -0
- vispy/app/tests/test_simultaneous.py +134 -0
- vispy/app/timer.py +174 -0
- vispy/color/__init__.py +17 -0
- vispy/color/_color_dict.py +193 -0
- vispy/color/color_array.py +447 -0
- vispy/color/color_space.py +181 -0
- vispy/color/colormap.py +1134 -0
- vispy/color/tests/__init__.py +0 -0
- vispy/color/tests/test_color.py +352 -0
- vispy/conftest.py +12 -0
- vispy/ext/__init__.py +0 -0
- vispy/ext/cocoapy.py +1542 -0
- vispy/ext/cubehelix.py +138 -0
- vispy/ext/egl.py +375 -0
- vispy/ext/fontconfig.py +118 -0
- vispy/ext/gdi32plus.py +206 -0
- vispy/ext/osmesa.py +105 -0
- vispy/geometry/__init__.py +23 -0
- vispy/geometry/_triangulation_debugger.py +171 -0
- vispy/geometry/calculations.py +134 -0
- vispy/geometry/curves.py +399 -0
- vispy/geometry/generation.py +643 -0
- vispy/geometry/isocurve.py +175 -0
- vispy/geometry/isosurface.py +465 -0
- vispy/geometry/meshdata.py +698 -0
- vispy/geometry/normals.py +78 -0
- vispy/geometry/parametric.py +56 -0
- vispy/geometry/polygon.py +137 -0
- vispy/geometry/rect.py +210 -0
- vispy/geometry/tests/__init__.py +0 -0
- vispy/geometry/tests/test_calculations.py +23 -0
- vispy/geometry/tests/test_generation.py +56 -0
- vispy/geometry/tests/test_meshdata.py +106 -0
- vispy/geometry/tests/test_triangulation.py +506 -0
- vispy/geometry/torusknot.py +142 -0
- vispy/geometry/triangulation.py +876 -0
- vispy/gloo/__init__.py +56 -0
- vispy/gloo/buffer.py +505 -0
- vispy/gloo/context.py +272 -0
- vispy/gloo/framebuffer.py +257 -0
- vispy/gloo/gl/__init__.py +234 -0
- vispy/gloo/gl/_constants.py +332 -0
- vispy/gloo/gl/_es2.py +986 -0
- vispy/gloo/gl/_gl2.py +1365 -0
- vispy/gloo/gl/_proxy.py +499 -0
- vispy/gloo/gl/_pyopengl2.py +362 -0
- vispy/gloo/gl/dummy.py +24 -0
- vispy/gloo/gl/es2.py +62 -0
- vispy/gloo/gl/gl2.py +98 -0
- vispy/gloo/gl/glplus.py +168 -0
- vispy/gloo/gl/pyopengl2.py +97 -0
- vispy/gloo/gl/tests/__init__.py +0 -0
- vispy/gloo/gl/tests/test_basics.py +282 -0
- vispy/gloo/gl/tests/test_functionality.py +566 -0
- vispy/gloo/gl/tests/test_names.py +246 -0
- vispy/gloo/gl/tests/test_use.py +71 -0
- vispy/gloo/glir.py +1816 -0
- vispy/gloo/globject.py +101 -0
- vispy/gloo/preprocessor.py +67 -0
- vispy/gloo/program.py +543 -0
- vispy/gloo/tests/__init__.py +0 -0
- vispy/gloo/tests/test_buffer.py +558 -0
- vispy/gloo/tests/test_context.py +119 -0
- vispy/gloo/tests/test_framebuffer.py +195 -0
- vispy/gloo/tests/test_glir.py +307 -0
- vispy/gloo/tests/test_globject.py +35 -0
- vispy/gloo/tests/test_program.py +302 -0
- vispy/gloo/tests/test_texture.py +732 -0
- vispy/gloo/tests/test_use_gloo.py +187 -0
- vispy/gloo/tests/test_util.py +60 -0
- vispy/gloo/tests/test_wrappers.py +261 -0
- vispy/gloo/texture.py +1045 -0
- vispy/gloo/util.py +129 -0
- vispy/gloo/wrappers.py +762 -0
- vispy/glsl/__init__.py +42 -0
- vispy/glsl/antialias/antialias.glsl +7 -0
- vispy/glsl/antialias/cap-butt.glsl +31 -0
- vispy/glsl/antialias/cap-round.glsl +29 -0
- vispy/glsl/antialias/cap-square.glsl +30 -0
- vispy/glsl/antialias/cap-triangle-in.glsl +30 -0
- vispy/glsl/antialias/cap-triangle-out.glsl +30 -0
- vispy/glsl/antialias/cap.glsl +67 -0
- vispy/glsl/antialias/caps.glsl +67 -0
- vispy/glsl/antialias/filled.glsl +50 -0
- vispy/glsl/antialias/outline.glsl +40 -0
- vispy/glsl/antialias/stroke.glsl +43 -0
- vispy/glsl/arrowheads/angle.glsl +99 -0
- vispy/glsl/arrowheads/arrowheads.frag +60 -0
- vispy/glsl/arrowheads/arrowheads.glsl +12 -0
- vispy/glsl/arrowheads/arrowheads.vert +83 -0
- vispy/glsl/arrowheads/curved.glsl +48 -0
- vispy/glsl/arrowheads/inhibitor.glsl +26 -0
- vispy/glsl/arrowheads/stealth.glsl +46 -0
- vispy/glsl/arrowheads/triangle.glsl +97 -0
- vispy/glsl/arrowheads/util.glsl +13 -0
- vispy/glsl/arrows/angle-30.glsl +12 -0
- vispy/glsl/arrows/angle-60.glsl +12 -0
- vispy/glsl/arrows/angle-90.glsl +12 -0
- vispy/glsl/arrows/arrow.frag +39 -0
- vispy/glsl/arrows/arrow.vert +49 -0
- vispy/glsl/arrows/arrows.glsl +17 -0
- vispy/glsl/arrows/common.glsl +187 -0
- vispy/glsl/arrows/curved.glsl +63 -0
- vispy/glsl/arrows/stealth.glsl +50 -0
- vispy/glsl/arrows/triangle-30.glsl +12 -0
- vispy/glsl/arrows/triangle-60.glsl +12 -0
- vispy/glsl/arrows/triangle-90.glsl +12 -0
- vispy/glsl/arrows/util.glsl +98 -0
- vispy/glsl/build_spatial_filters.py +660 -0
- vispy/glsl/collections/agg-fast-path.frag +20 -0
- vispy/glsl/collections/agg-fast-path.vert +78 -0
- vispy/glsl/collections/agg-glyph.frag +60 -0
- vispy/glsl/collections/agg-glyph.vert +33 -0
- vispy/glsl/collections/agg-marker.frag +35 -0
- vispy/glsl/collections/agg-marker.vert +48 -0
- vispy/glsl/collections/agg-path.frag +55 -0
- vispy/glsl/collections/agg-path.vert +166 -0
- vispy/glsl/collections/agg-point.frag +21 -0
- vispy/glsl/collections/agg-point.vert +35 -0
- vispy/glsl/collections/agg-segment.frag +32 -0
- vispy/glsl/collections/agg-segment.vert +75 -0
- vispy/glsl/collections/marker.frag +38 -0
- vispy/glsl/collections/marker.vert +48 -0
- vispy/glsl/collections/raw-path.frag +15 -0
- vispy/glsl/collections/raw-path.vert +24 -0
- vispy/glsl/collections/raw-point.frag +14 -0
- vispy/glsl/collections/raw-point.vert +31 -0
- vispy/glsl/collections/raw-segment.frag +18 -0
- vispy/glsl/collections/raw-segment.vert +26 -0
- vispy/glsl/collections/raw-triangle.frag +13 -0
- vispy/glsl/collections/raw-triangle.vert +26 -0
- vispy/glsl/collections/sdf-glyph-ticks.vert +69 -0
- vispy/glsl/collections/sdf-glyph.frag +80 -0
- vispy/glsl/collections/sdf-glyph.vert +59 -0
- vispy/glsl/collections/tick-labels.vert +71 -0
- vispy/glsl/colormaps/autumn.glsl +20 -0
- vispy/glsl/colormaps/blues.glsl +20 -0
- vispy/glsl/colormaps/color-space.glsl +17 -0
- vispy/glsl/colormaps/colormaps.glsl +24 -0
- vispy/glsl/colormaps/cool.glsl +20 -0
- vispy/glsl/colormaps/fire.glsl +21 -0
- vispy/glsl/colormaps/gray.glsl +20 -0
- vispy/glsl/colormaps/greens.glsl +20 -0
- vispy/glsl/colormaps/hot.glsl +22 -0
- vispy/glsl/colormaps/ice.glsl +20 -0
- vispy/glsl/colormaps/icefire.glsl +23 -0
- vispy/glsl/colormaps/parse.py +40 -0
- vispy/glsl/colormaps/reds.glsl +20 -0
- vispy/glsl/colormaps/spring.glsl +20 -0
- vispy/glsl/colormaps/summer.glsl +20 -0
- vispy/glsl/colormaps/user.glsl +22 -0
- vispy/glsl/colormaps/util.glsl +41 -0
- vispy/glsl/colormaps/wheel.glsl +21 -0
- vispy/glsl/colormaps/winter.glsl +20 -0
- vispy/glsl/lines/agg.frag +320 -0
- vispy/glsl/lines/agg.vert +241 -0
- vispy/glsl/markers/arrow.glsl +12 -0
- vispy/glsl/markers/asterisk.glsl +16 -0
- vispy/glsl/markers/chevron.glsl +14 -0
- vispy/glsl/markers/clover.glsl +20 -0
- vispy/glsl/markers/club.glsl +31 -0
- vispy/glsl/markers/cross.glsl +17 -0
- vispy/glsl/markers/diamond.glsl +12 -0
- vispy/glsl/markers/disc.glsl +9 -0
- vispy/glsl/markers/ellipse.glsl +67 -0
- vispy/glsl/markers/hbar.glsl +9 -0
- vispy/glsl/markers/heart.glsl +15 -0
- vispy/glsl/markers/infinity.glsl +15 -0
- vispy/glsl/markers/marker-sdf.frag +74 -0
- vispy/glsl/markers/marker-sdf.vert +41 -0
- vispy/glsl/markers/marker.frag +36 -0
- vispy/glsl/markers/marker.vert +46 -0
- vispy/glsl/markers/markers.glsl +24 -0
- vispy/glsl/markers/pin.glsl +18 -0
- vispy/glsl/markers/ring.glsl +11 -0
- vispy/glsl/markers/spade.glsl +28 -0
- vispy/glsl/markers/square.glsl +10 -0
- vispy/glsl/markers/tag.glsl +11 -0
- vispy/glsl/markers/triangle.glsl +14 -0
- vispy/glsl/markers/vbar.glsl +9 -0
- vispy/glsl/math/circle-through-2-points.glsl +30 -0
- vispy/glsl/math/constants.glsl +48 -0
- vispy/glsl/math/double.glsl +114 -0
- vispy/glsl/math/functions.glsl +20 -0
- vispy/glsl/math/point-to-line-distance.glsl +31 -0
- vispy/glsl/math/point-to-line-projection.glsl +29 -0
- vispy/glsl/math/signed-line-distance.glsl +27 -0
- vispy/glsl/math/signed-segment-distance.glsl +30 -0
- vispy/glsl/misc/regular-grid.frag +244 -0
- vispy/glsl/misc/spatial-filters.frag +1407 -0
- vispy/glsl/misc/viewport-NDC.glsl +20 -0
- vispy/glsl/transforms/azimuthal-equal-area.glsl +32 -0
- vispy/glsl/transforms/azimuthal-equidistant.glsl +38 -0
- vispy/glsl/transforms/hammer.glsl +44 -0
- vispy/glsl/transforms/identity.glsl +6 -0
- vispy/glsl/transforms/identity_forward.glsl +23 -0
- vispy/glsl/transforms/identity_inverse.glsl +23 -0
- vispy/glsl/transforms/linear-scale.glsl +127 -0
- vispy/glsl/transforms/log-scale.glsl +126 -0
- vispy/glsl/transforms/mercator-transverse-forward.glsl +40 -0
- vispy/glsl/transforms/mercator-transverse-inverse.glsl +40 -0
- vispy/glsl/transforms/panzoom.glsl +10 -0
- vispy/glsl/transforms/polar.glsl +41 -0
- vispy/glsl/transforms/position.glsl +44 -0
- vispy/glsl/transforms/power-scale.glsl +139 -0
- vispy/glsl/transforms/projection.glsl +7 -0
- vispy/glsl/transforms/pvm.glsl +13 -0
- vispy/glsl/transforms/rotate.glsl +45 -0
- vispy/glsl/transforms/trackball.glsl +15 -0
- vispy/glsl/transforms/translate.glsl +35 -0
- vispy/glsl/transforms/transverse_mercator.glsl +38 -0
- vispy/glsl/transforms/viewport-clipping.glsl +14 -0
- vispy/glsl/transforms/viewport-transform.glsl +16 -0
- vispy/glsl/transforms/viewport.glsl +50 -0
- vispy/glsl/transforms/x.glsl +24 -0
- vispy/glsl/transforms/y.glsl +19 -0
- vispy/glsl/transforms/z.glsl +14 -0
- vispy/io/__init__.py +20 -0
- vispy/io/_data/spatial-filters.npy +0 -0
- vispy/io/datasets.py +94 -0
- vispy/io/image.py +231 -0
- vispy/io/mesh.py +122 -0
- vispy/io/stl.py +167 -0
- vispy/io/tests/__init__.py +0 -0
- vispy/io/tests/test_image.py +47 -0
- vispy/io/tests/test_io.py +121 -0
- vispy/io/wavefront.py +350 -0
- vispy/plot/__init__.py +36 -0
- vispy/plot/fig.py +58 -0
- vispy/plot/plotwidget.py +522 -0
- vispy/plot/tests/__init__.py +0 -0
- vispy/plot/tests/test_plot.py +46 -0
- vispy/scene/__init__.py +43 -0
- vispy/scene/cameras/__init__.py +27 -0
- vispy/scene/cameras/_base.py +38 -0
- vispy/scene/cameras/arcball.py +106 -0
- vispy/scene/cameras/base_camera.py +538 -0
- vispy/scene/cameras/fly.py +474 -0
- vispy/scene/cameras/magnify.py +163 -0
- vispy/scene/cameras/panzoom.py +308 -0
- vispy/scene/cameras/perspective.py +333 -0
- vispy/scene/cameras/tests/__init__.py +0 -0
- vispy/scene/cameras/tests/test_cameras.py +27 -0
- vispy/scene/cameras/tests/test_link.py +53 -0
- vispy/scene/cameras/tests/test_perspective.py +122 -0
- vispy/scene/cameras/turntable.py +173 -0
- vispy/scene/canvas.py +639 -0
- vispy/scene/events.py +85 -0
- vispy/scene/node.py +644 -0
- vispy/scene/subscene.py +20 -0
- vispy/scene/tests/__init__.py +0 -0
- vispy/scene/tests/test_canvas.py +119 -0
- vispy/scene/tests/test_node.py +142 -0
- vispy/scene/tests/test_visuals.py +141 -0
- vispy/scene/visuals.py +276 -0
- vispy/scene/widgets/__init__.py +18 -0
- vispy/scene/widgets/anchor.py +25 -0
- vispy/scene/widgets/axis.py +88 -0
- vispy/scene/widgets/colorbar.py +176 -0
- vispy/scene/widgets/console.py +351 -0
- vispy/scene/widgets/grid.py +509 -0
- vispy/scene/widgets/label.py +50 -0
- vispy/scene/widgets/tests/__init__.py +0 -0
- vispy/scene/widgets/tests/test_colorbar.py +47 -0
- vispy/scene/widgets/viewbox.py +199 -0
- vispy/scene/widgets/widget.py +478 -0
- vispy/testing/__init__.py +51 -0
- vispy/testing/_runners.py +446 -0
- vispy/testing/_testing.py +416 -0
- vispy/testing/image_tester.py +473 -0
- vispy/testing/rendered_array_tester.py +85 -0
- vispy/testing/tests/__init__.py +0 -0
- vispy/testing/tests/test_testing.py +20 -0
- vispy/util/__init__.py +17 -0
- vispy/util/bunch.py +15 -0
- vispy/util/check_environment.py +57 -0
- vispy/util/config.py +490 -0
- vispy/util/dpi/__init__.py +19 -0
- vispy/util/dpi/_linux.py +69 -0
- vispy/util/dpi/_quartz.py +26 -0
- vispy/util/dpi/_win32.py +34 -0
- vispy/util/dpi/tests/__init__.py +0 -0
- vispy/util/dpi/tests/test_dpi.py +16 -0
- vispy/util/eq.py +41 -0
- vispy/util/event.py +774 -0
- vispy/util/fetching.py +276 -0
- vispy/util/filter.py +44 -0
- vispy/util/fonts/__init__.py +14 -0
- vispy/util/fonts/_freetype.py +73 -0
- vispy/util/fonts/_quartz.py +192 -0
- vispy/util/fonts/_triage.py +36 -0
- vispy/util/fonts/_vispy_fonts.py +20 -0
- vispy/util/fonts/_win32.py +105 -0
- vispy/util/fonts/data/OpenSans-Bold.ttf +0 -0
- vispy/util/fonts/data/OpenSans-BoldItalic.ttf +0 -0
- vispy/util/fonts/data/OpenSans-Italic.ttf +0 -0
- vispy/util/fonts/data/OpenSans-Regular.ttf +0 -0
- vispy/util/fonts/tests/__init__.py +0 -0
- vispy/util/fonts/tests/test_font.py +45 -0
- vispy/util/fourier.py +69 -0
- vispy/util/frozen.py +25 -0
- vispy/util/gallery_scraper.py +268 -0
- vispy/util/keys.py +91 -0
- vispy/util/logs.py +358 -0
- vispy/util/osmesa_gl.py +17 -0
- vispy/util/profiler.py +135 -0
- vispy/util/ptime.py +16 -0
- vispy/util/quaternion.py +229 -0
- vispy/util/svg/__init__.py +18 -0
- vispy/util/svg/base.py +20 -0
- vispy/util/svg/color.py +219 -0
- vispy/util/svg/element.py +51 -0
- vispy/util/svg/geometry.py +478 -0
- vispy/util/svg/group.py +66 -0
- vispy/util/svg/length.py +81 -0
- vispy/util/svg/number.py +25 -0
- vispy/util/svg/path.py +332 -0
- vispy/util/svg/shapes.py +57 -0
- vispy/util/svg/style.py +59 -0
- vispy/util/svg/svg.py +40 -0
- vispy/util/svg/transform.py +223 -0
- vispy/util/svg/transformable.py +28 -0
- vispy/util/svg/viewport.py +73 -0
- vispy/util/tests/__init__.py +0 -0
- vispy/util/tests/test_config.py +58 -0
- vispy/util/tests/test_docstring_parameters.py +123 -0
- vispy/util/tests/test_emitter_group.py +262 -0
- vispy/util/tests/test_event_emitter.py +743 -0
- vispy/util/tests/test_fourier.py +35 -0
- vispy/util/tests/test_gallery_scraper.py +112 -0
- vispy/util/tests/test_import.py +127 -0
- vispy/util/tests/test_key.py +22 -0
- vispy/util/tests/test_logging.py +45 -0
- vispy/util/tests/test_run.py +14 -0
- vispy/util/tests/test_transforms.py +42 -0
- vispy/util/tests/test_vispy.py +48 -0
- vispy/util/transforms.py +201 -0
- vispy/util/wrappers.py +155 -0
- vispy/version.py +4 -0
- vispy/visuals/__init__.py +50 -0
- vispy/visuals/_scalable_textures.py +485 -0
- vispy/visuals/axis.py +678 -0
- vispy/visuals/border.py +208 -0
- vispy/visuals/box.py +79 -0
- vispy/visuals/collections/__init__.py +30 -0
- vispy/visuals/collections/agg_fast_path_collection.py +219 -0
- vispy/visuals/collections/agg_path_collection.py +197 -0
- vispy/visuals/collections/agg_point_collection.py +52 -0
- vispy/visuals/collections/agg_segment_collection.py +142 -0
- vispy/visuals/collections/array_list.py +401 -0
- vispy/visuals/collections/base_collection.py +482 -0
- vispy/visuals/collections/collection.py +253 -0
- vispy/visuals/collections/path_collection.py +23 -0
- vispy/visuals/collections/point_collection.py +19 -0
- vispy/visuals/collections/polygon_collection.py +25 -0
- vispy/visuals/collections/raw_path_collection.py +119 -0
- vispy/visuals/collections/raw_point_collection.py +113 -0
- vispy/visuals/collections/raw_polygon_collection.py +77 -0
- vispy/visuals/collections/raw_segment_collection.py +112 -0
- vispy/visuals/collections/raw_triangle_collection.py +78 -0
- vispy/visuals/collections/segment_collection.py +19 -0
- vispy/visuals/collections/triangle_collection.py +16 -0
- vispy/visuals/collections/util.py +168 -0
- vispy/visuals/colorbar.py +699 -0
- vispy/visuals/cube.py +41 -0
- vispy/visuals/ellipse.py +163 -0
- vispy/visuals/filters/__init__.py +10 -0
- vispy/visuals/filters/base_filter.py +242 -0
- vispy/visuals/filters/clipper.py +60 -0
- vispy/visuals/filters/clipping_planes.py +122 -0
- vispy/visuals/filters/color.py +181 -0
- vispy/visuals/filters/markers.py +28 -0
- vispy/visuals/filters/mesh.py +796 -0
- vispy/visuals/filters/picking.py +60 -0
- vispy/visuals/filters/tests/__init__.py +3 -0
- vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
- vispy/visuals/filters/tests/test_wireframe_filter.py +16 -0
- vispy/visuals/glsl/__init__.py +1 -0
- vispy/visuals/glsl/antialiasing.py +133 -0
- vispy/visuals/glsl/color.py +63 -0
- vispy/visuals/graphs/__init__.py +1 -0
- vispy/visuals/graphs/graph.py +240 -0
- vispy/visuals/graphs/layouts/__init__.py +55 -0
- vispy/visuals/graphs/layouts/circular.py +49 -0
- vispy/visuals/graphs/layouts/force_directed.py +211 -0
- vispy/visuals/graphs/layouts/networkx_layout.py +87 -0
- vispy/visuals/graphs/layouts/random.py +52 -0
- vispy/visuals/graphs/tests/__init__.py +1 -0
- vispy/visuals/graphs/tests/test_layouts.py +139 -0
- vispy/visuals/graphs/tests/test_networkx_layout.py +47 -0
- vispy/visuals/graphs/util.py +120 -0
- vispy/visuals/gridlines.py +105 -0
- vispy/visuals/gridmesh.py +98 -0
- vispy/visuals/histogram.py +58 -0
- vispy/visuals/image.py +688 -0
- vispy/visuals/image_complex.py +130 -0
- vispy/visuals/infinite_line.py +199 -0
- vispy/visuals/instanced_mesh.py +152 -0
- vispy/visuals/isocurve.py +213 -0
- vispy/visuals/isoline.py +241 -0
- vispy/visuals/isosurface.py +113 -0
- vispy/visuals/line/__init__.py +6 -0
- vispy/visuals/line/arrow.py +289 -0
- vispy/visuals/line/dash_atlas.py +90 -0
- vispy/visuals/line/line.py +545 -0
- vispy/visuals/line_plot.py +135 -0
- vispy/visuals/linear_region.py +199 -0
- vispy/visuals/markers.py +810 -0
- vispy/visuals/mesh.py +373 -0
- vispy/visuals/mesh_normals.py +159 -0
- vispy/visuals/plane.py +54 -0
- vispy/visuals/polygon.py +145 -0
- vispy/visuals/rectangle.py +196 -0
- vispy/visuals/regular_polygon.py +56 -0
- vispy/visuals/scrolling_lines.py +197 -0
- vispy/visuals/shaders/__init__.py +17 -0
- vispy/visuals/shaders/compiler.py +206 -0
- vispy/visuals/shaders/expression.py +99 -0
- vispy/visuals/shaders/function.py +788 -0
- vispy/visuals/shaders/multiprogram.py +145 -0
- vispy/visuals/shaders/parsing.py +140 -0
- vispy/visuals/shaders/program.py +161 -0
- vispy/visuals/shaders/shader_object.py +162 -0
- vispy/visuals/shaders/tests/__init__.py +0 -0
- vispy/visuals/shaders/tests/test_function.py +486 -0
- vispy/visuals/shaders/tests/test_multiprogram.py +78 -0
- vispy/visuals/shaders/tests/test_parsing.py +57 -0
- vispy/visuals/shaders/variable.py +272 -0
- vispy/visuals/spectrogram.py +169 -0
- vispy/visuals/sphere.py +80 -0
- vispy/visuals/surface_plot.py +192 -0
- vispy/visuals/tests/__init__.py +0 -0
- vispy/visuals/tests/test_arrows.py +109 -0
- vispy/visuals/tests/test_axis.py +120 -0
- vispy/visuals/tests/test_collections.py +15 -0
- vispy/visuals/tests/test_colorbar.py +179 -0
- vispy/visuals/tests/test_colormap.py +97 -0
- vispy/visuals/tests/test_ellipse.py +122 -0
- vispy/visuals/tests/test_histogram.py +24 -0
- vispy/visuals/tests/test_image.py +390 -0
- vispy/visuals/tests/test_image_complex.py +36 -0
- vispy/visuals/tests/test_infinite_line.py +53 -0
- vispy/visuals/tests/test_instanced_mesh.py +50 -0
- vispy/visuals/tests/test_isosurface.py +22 -0
- vispy/visuals/tests/test_linear_region.py +152 -0
- vispy/visuals/tests/test_markers.py +54 -0
- vispy/visuals/tests/test_mesh.py +261 -0
- vispy/visuals/tests/test_mesh_normals.py +218 -0
- vispy/visuals/tests/test_polygon.py +112 -0
- vispy/visuals/tests/test_rectangle.py +163 -0
- vispy/visuals/tests/test_regular_polygon.py +111 -0
- vispy/visuals/tests/test_scalable_textures.py +180 -0
- vispy/visuals/tests/test_sdf.py +73 -0
- vispy/visuals/tests/test_spectrogram.py +42 -0
- vispy/visuals/tests/test_text.py +95 -0
- vispy/visuals/tests/test_volume.py +542 -0
- vispy/visuals/tests/test_windbarb.py +33 -0
- vispy/visuals/text/__init__.py +7 -0
- vispy/visuals/text/_sdf_cpu.cpython-312-aarch64-linux-gnu.so +0 -0
- vispy/visuals/text/_sdf_cpu.pyx +110 -0
- vispy/visuals/text/_sdf_gpu.py +316 -0
- vispy/visuals/text/text.py +675 -0
- vispy/visuals/transforms/__init__.py +34 -0
- vispy/visuals/transforms/_util.py +191 -0
- vispy/visuals/transforms/base_transform.py +233 -0
- vispy/visuals/transforms/chain.py +300 -0
- vispy/visuals/transforms/interactive.py +98 -0
- vispy/visuals/transforms/linear.py +564 -0
- vispy/visuals/transforms/nonlinear.py +398 -0
- vispy/visuals/transforms/tests/__init__.py +0 -0
- vispy/visuals/transforms/tests/test_transforms.py +243 -0
- vispy/visuals/transforms/transform_system.py +339 -0
- vispy/visuals/tube.py +173 -0
- vispy/visuals/visual.py +923 -0
- vispy/visuals/volume.py +1335 -0
- vispy/visuals/windbarb.py +291 -0
- vispy/visuals/xyz_axis.py +34 -0
- vispy-0.14.0.dist-info/LICENSE.txt +36 -0
- vispy-0.14.0.dist-info/METADATA +218 -0
- vispy-0.14.0.dist-info/RECORD +519 -0
- vispy-0.14.0.dist-info/WHEEL +6 -0
- vispy-0.14.0.dist-info/top_level.txt +1 -0
vispy/util/wrappers.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
|
+
"""
|
|
5
|
+
Some wrappers to avoid circular imports, or make certain calls easier.
|
|
6
|
+
|
|
7
|
+
The idea of a 'global' vispy.use function is that although vispy.app
|
|
8
|
+
and vispy.gloo.gl can be used independently, they are not complely
|
|
9
|
+
independent for some configureation. E.g. when using real ES 2.0,
|
|
10
|
+
the app backend should use EGL and not a desktop OpenGL context. Also,
|
|
11
|
+
we probably want it to be easy to configure vispy to use the ipython
|
|
12
|
+
notebook backend, which requires specific config of both app and gl.
|
|
13
|
+
|
|
14
|
+
This module does not have to be aware of the available app and gl
|
|
15
|
+
backends, but it should be(come) aware of (in)compatibilities between
|
|
16
|
+
them.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import subprocess
|
|
20
|
+
from .config import _get_args
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def use(app=None, gl=None):
|
|
24
|
+
"""Set the usage options for vispy
|
|
25
|
+
|
|
26
|
+
Specify what app backend and GL backend to use.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
app : str
|
|
31
|
+
The app backend to use (case insensitive). Standard backends:
|
|
32
|
+
* 'PyQt4': use Qt widget toolkit via PyQt4.
|
|
33
|
+
* 'PyQt5': use Qt widget toolkit via PyQt5.
|
|
34
|
+
* 'PyQt6': use Qt widget toolkit via PyQt6.
|
|
35
|
+
* 'PySide': use Qt widget toolkit via PySide.
|
|
36
|
+
* 'PySide2': use Qt widget toolkit via PySide2.
|
|
37
|
+
* 'PySide6': use Qt widget toolkit via PySide6.
|
|
38
|
+
* 'PyGlet': use Pyglet backend.
|
|
39
|
+
* 'Glfw': use Glfw backend (successor of Glut). Widely available
|
|
40
|
+
on Linux.
|
|
41
|
+
* 'SDL2': use SDL v2 backend.
|
|
42
|
+
* 'osmesa': Use OSMesa backend
|
|
43
|
+
Additional backends:
|
|
44
|
+
* 'jupyter_rfb': show vispy canvases in Jupyter lab/notebook
|
|
45
|
+
(depends on the jupyter_rfb library).
|
|
46
|
+
|
|
47
|
+
gl : str
|
|
48
|
+
The gl backend to use (case insensitive). Options are:
|
|
49
|
+
* 'gl2': use Vispy's desktop OpenGL API.
|
|
50
|
+
* 'pyopengl2': use PyOpenGL's desktop OpenGL API. Mostly for
|
|
51
|
+
testing.
|
|
52
|
+
* 'es2': (TO COME) use real OpenGL ES 2.0 on Windows via Angle.
|
|
53
|
+
Availability of ES 2.0 is larger for Windows, since it relies
|
|
54
|
+
on DirectX.
|
|
55
|
+
* 'gl+': use the full OpenGL functionality available on
|
|
56
|
+
your system (via PyOpenGL).
|
|
57
|
+
|
|
58
|
+
Notes
|
|
59
|
+
-----
|
|
60
|
+
If the app option is given, ``vispy.app.use_app()`` is called. If
|
|
61
|
+
the gl option is given, ``vispy.gloo.use_gl()`` is called.
|
|
62
|
+
|
|
63
|
+
If an app backend name is provided, and that backend could not be
|
|
64
|
+
loaded, an error is raised.
|
|
65
|
+
|
|
66
|
+
If no backend name is provided, Vispy will first check if the GUI
|
|
67
|
+
toolkit corresponding to each backend is already imported, and try
|
|
68
|
+
that backend first. If this is unsuccessful, it will try the
|
|
69
|
+
'default_backend' provided in the vispy config. If still not
|
|
70
|
+
succesful, it will try each backend in a predetermined order.
|
|
71
|
+
|
|
72
|
+
See Also
|
|
73
|
+
--------
|
|
74
|
+
vispy.app.use_app
|
|
75
|
+
vispy.gloo.gl.use_gl
|
|
76
|
+
"""
|
|
77
|
+
if app is None and gl is None:
|
|
78
|
+
raise TypeError('Must specify at least one of "app" or "gl".')
|
|
79
|
+
|
|
80
|
+
if app == 'osmesa':
|
|
81
|
+
from ..util.osmesa_gl import fix_osmesa_gl_lib
|
|
82
|
+
fix_osmesa_gl_lib()
|
|
83
|
+
if gl is not None:
|
|
84
|
+
raise ValueError("Do not specify gl when using osmesa")
|
|
85
|
+
|
|
86
|
+
# Apply now
|
|
87
|
+
if gl:
|
|
88
|
+
from .. import gloo, config
|
|
89
|
+
config['gl_backend'] = gl
|
|
90
|
+
gloo.gl.use_gl(gl)
|
|
91
|
+
if app:
|
|
92
|
+
from ..app import use_app
|
|
93
|
+
use_app(app)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def run_subprocess(command, return_code=False, **kwargs):
|
|
97
|
+
"""Run command using subprocess.Popen
|
|
98
|
+
|
|
99
|
+
Run command and wait for command to complete. If the return code was zero
|
|
100
|
+
then return, otherwise raise CalledProcessError.
|
|
101
|
+
By default, this will also add stdout= and stderr=subproces.PIPE
|
|
102
|
+
to the call to Popen to suppress printing to the terminal.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
command : list of str
|
|
107
|
+
Command to run as subprocess (see subprocess.Popen documentation).
|
|
108
|
+
return_code : bool
|
|
109
|
+
If True, the returncode will be returned, and no error checking
|
|
110
|
+
will be performed (so this function should always return without
|
|
111
|
+
error).
|
|
112
|
+
**kwargs : dict
|
|
113
|
+
Additional kwargs to pass to ``subprocess.Popen``.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
stdout : str
|
|
118
|
+
Stdout returned by the process.
|
|
119
|
+
stderr : str
|
|
120
|
+
Stderr returned by the process.
|
|
121
|
+
code : int
|
|
122
|
+
The command exit code. Only returned if ``return_code`` is True.
|
|
123
|
+
"""
|
|
124
|
+
# code adapted with permission from mne-python
|
|
125
|
+
use_kwargs = dict(stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
126
|
+
use_kwargs.update(kwargs)
|
|
127
|
+
|
|
128
|
+
p = subprocess.Popen(command, **use_kwargs)
|
|
129
|
+
output = p.communicate()
|
|
130
|
+
|
|
131
|
+
# communicate() may return bytes, str, or None depending on the kwargs
|
|
132
|
+
# passed to Popen(). Convert all to unicode str:
|
|
133
|
+
output = ['' if s is None else s for s in output]
|
|
134
|
+
output = [s.decode('utf-8') if isinstance(s, bytes) else s for s in output]
|
|
135
|
+
output = tuple(output)
|
|
136
|
+
|
|
137
|
+
if not return_code and p.returncode:
|
|
138
|
+
print(output[0])
|
|
139
|
+
print(output[1])
|
|
140
|
+
err_fun = subprocess.CalledProcessError.__init__
|
|
141
|
+
if 'output' in _get_args(err_fun):
|
|
142
|
+
raise subprocess.CalledProcessError(p.returncode, command, output)
|
|
143
|
+
else:
|
|
144
|
+
raise subprocess.CalledProcessError(p.returncode, command)
|
|
145
|
+
if return_code:
|
|
146
|
+
output = output + (p.returncode,)
|
|
147
|
+
return output
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test(*args, **kwargs):
|
|
151
|
+
"""Proxy function to delay `.testing` import"""
|
|
152
|
+
from vispy.testing import test as _test # noqa
|
|
153
|
+
return _test(*args, **kwargs)
|
|
154
|
+
|
|
155
|
+
test.__test__ = False # no discover test function as test
|
vispy/version.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
|
+
"""
|
|
5
|
+
This module provides a library of Visual classes, which are drawable objects
|
|
6
|
+
intended to encapsulate simple graphic objects such as lines, meshes, points,
|
|
7
|
+
2D shapes, images, text, etc.
|
|
8
|
+
|
|
9
|
+
These classes define only the OpenGL machinery and connot be used directly in
|
|
10
|
+
a scenegraph. For scenegraph use, see the complementary Visual+Node classes
|
|
11
|
+
defined in vispy.scene.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .axis import AxisVisual # noqa
|
|
15
|
+
from .box import BoxVisual # noqa
|
|
16
|
+
from .cube import CubeVisual # noqa
|
|
17
|
+
from .ellipse import EllipseVisual # noqa
|
|
18
|
+
from .gridlines import GridLinesVisual # noqa
|
|
19
|
+
from .image import ImageVisual # noqa
|
|
20
|
+
from .image_complex import ComplexImageVisual # noqa
|
|
21
|
+
from .gridmesh import GridMeshVisual # noqa
|
|
22
|
+
from .histogram import HistogramVisual # noqa
|
|
23
|
+
from .infinite_line import InfiniteLineVisual # noqa
|
|
24
|
+
from .instanced_mesh import InstancedMeshVisual # noqa
|
|
25
|
+
from .isocurve import IsocurveVisual # noqa
|
|
26
|
+
from .isoline import IsolineVisual # noqa
|
|
27
|
+
from .isosurface import IsosurfaceVisual # noqa
|
|
28
|
+
from .line import LineVisual, ArrowVisual # noqa
|
|
29
|
+
from .linear_region import LinearRegionVisual # noqa
|
|
30
|
+
from .line_plot import LinePlotVisual # noqa
|
|
31
|
+
from .markers import MarkersVisual # noqa
|
|
32
|
+
from .mesh import MeshVisual # noqa
|
|
33
|
+
from .mesh_normals import MeshNormalsVisual # noqa
|
|
34
|
+
from .plane import PlaneVisual # noqa
|
|
35
|
+
from .polygon import PolygonVisual # noqa
|
|
36
|
+
from .rectangle import RectangleVisual # noqa
|
|
37
|
+
from .regular_polygon import RegularPolygonVisual # noqa
|
|
38
|
+
from .scrolling_lines import ScrollingLinesVisual # noqa
|
|
39
|
+
from .spectrogram import SpectrogramVisual # noqa
|
|
40
|
+
from .sphere import SphereVisual # noqa
|
|
41
|
+
from .surface_plot import SurfacePlotVisual # noqa
|
|
42
|
+
from .text import TextVisual # noqa
|
|
43
|
+
from .tube import TubeVisual # noqa
|
|
44
|
+
from .visual import BaseVisual, Visual, CompoundVisual # noqa
|
|
45
|
+
from .volume import VolumeVisual # noqa
|
|
46
|
+
from .xyz_axis import XYZAxisVisual # noqa
|
|
47
|
+
from .border import _BorderVisual # noqa
|
|
48
|
+
from .colorbar import ColorBarVisual # noqa
|
|
49
|
+
from .graphs import GraphVisual # noqa
|
|
50
|
+
from .windbarb import WindbarbVisual # noqa
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from vispy.gloo.texture import Texture2D, Texture3D, convert_dtype_and_clip
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_default_clim_from_dtype(dtype):
|
|
12
|
+
"""Get min and max color limits based on the range of the dtype."""
|
|
13
|
+
# assume floating point data is pre-normalized to 0 and 1
|
|
14
|
+
if np.issubdtype(dtype, np.floating):
|
|
15
|
+
return 0, 1
|
|
16
|
+
# assume integer RGBs fill the whole data space
|
|
17
|
+
dtype_info = np.iinfo(dtype)
|
|
18
|
+
dmin = dtype_info.min
|
|
19
|
+
dmax = dtype_info.max
|
|
20
|
+
return dmin, dmax
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_default_clim_from_data(data):
|
|
24
|
+
"""Compute a reasonable clim from the min and max, taking nans into account.
|
|
25
|
+
|
|
26
|
+
If there are no non-finite values (nan, inf, -inf) this is as fast as it can be.
|
|
27
|
+
Otherwise, this functions is about 3x slower.
|
|
28
|
+
"""
|
|
29
|
+
# Fast
|
|
30
|
+
min_value = data.min()
|
|
31
|
+
max_value = data.max()
|
|
32
|
+
|
|
33
|
+
# Need more work? The nan-functions are slower
|
|
34
|
+
min_finite = np.isfinite(min_value)
|
|
35
|
+
max_finite = np.isfinite(max_value)
|
|
36
|
+
if not (min_finite and max_finite):
|
|
37
|
+
finite_data = data[np.isfinite(data)]
|
|
38
|
+
if finite_data.size:
|
|
39
|
+
min_value = finite_data.min()
|
|
40
|
+
max_value = finite_data.max()
|
|
41
|
+
else:
|
|
42
|
+
min_value = max_value = 0 # no finite values in the data
|
|
43
|
+
|
|
44
|
+
return min_value, max_value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class _ScaledTextureMixin:
|
|
48
|
+
"""Mixin class to make a texture aware of color limits.
|
|
49
|
+
|
|
50
|
+
This class contains the shared functionality for the CPU and GPU mixin
|
|
51
|
+
classes below. In some cases this class provides a "generic"
|
|
52
|
+
implementation of a specific method and is then overridden by one of the
|
|
53
|
+
subclasses.
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
data : ndarray | tuple | None
|
|
58
|
+
Texture data in the form of a numpy array. A tuple of the shape of the
|
|
59
|
+
texture can also be given. However, some subclasses may benefit from
|
|
60
|
+
or even require a numpy array to make decisions based on shape **and**
|
|
61
|
+
dtype.
|
|
62
|
+
**texture_kwargs
|
|
63
|
+
Any other keyword arguments to pass to the parent TextureXD class.
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, data=None, **texture_kwargs):
|
|
68
|
+
self._clim = None
|
|
69
|
+
self._data_dtype = None
|
|
70
|
+
data, texture_kwargs = self.init_scaling_texture(data, **texture_kwargs)
|
|
71
|
+
# Call the __init__ of the TextureXD class
|
|
72
|
+
super().__init__(data, **texture_kwargs)
|
|
73
|
+
|
|
74
|
+
def init_scaling_texture(self, data=None, internalformat=None, **texture_kwargs):
|
|
75
|
+
"""Initialize scaling properties and create a representative array."""
|
|
76
|
+
self._data_dtype = getattr(data, 'dtype', None)
|
|
77
|
+
data = self._create_rep_array(data)
|
|
78
|
+
internalformat = self._get_texture_format_for_data(
|
|
79
|
+
data,
|
|
80
|
+
internalformat)
|
|
81
|
+
texture_kwargs['internalformat'] = internalformat
|
|
82
|
+
return data, texture_kwargs
|
|
83
|
+
|
|
84
|
+
def _get_texture_format_for_data(self, data, internalformat):
|
|
85
|
+
return internalformat
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def clim(self):
|
|
89
|
+
"""Color limits of the texture's data."""
|
|
90
|
+
return self._clim
|
|
91
|
+
|
|
92
|
+
def set_clim(self, clim):
|
|
93
|
+
"""Set clim and return if a texture update is needed.
|
|
94
|
+
|
|
95
|
+
In this default implementation, it is assumed changing the color limit
|
|
96
|
+
never requires re-uploading the data to the texture (always return
|
|
97
|
+
``False``).
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
need_texture_upload = False
|
|
101
|
+
if isinstance(clim, str):
|
|
102
|
+
if clim != 'auto':
|
|
103
|
+
raise ValueError('clim must be "auto" if a string')
|
|
104
|
+
self._clim = clim
|
|
105
|
+
else:
|
|
106
|
+
try:
|
|
107
|
+
cmin, cmax = clim
|
|
108
|
+
except (ValueError, TypeError):
|
|
109
|
+
raise ValueError('clim must have two elements')
|
|
110
|
+
self._clim = (cmin, cmax)
|
|
111
|
+
return need_texture_upload
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def clim_normalized(self):
|
|
115
|
+
"""Normalize current clims to match texture data inside the shader.
|
|
116
|
+
|
|
117
|
+
If data is scaled on the CPU then the texture data will be in the range
|
|
118
|
+
0-1 in the _build_texture() method. Inside the fragment shader the
|
|
119
|
+
final contrast adjustment will be applied based on this normalized
|
|
120
|
+
``clim``.
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
if isinstance(self.clim, str) and self.clim == "auto":
|
|
124
|
+
raise RuntimeError("Can't return 'auto' normalized color limits "
|
|
125
|
+
"until data has been set. Call "
|
|
126
|
+
"'scale_and_set_data' first.")
|
|
127
|
+
if self._data_dtype is None:
|
|
128
|
+
raise RuntimeError("Can't return normalized color limits until "
|
|
129
|
+
"data has been set. Call "
|
|
130
|
+
"'scale_and_set_data' first.")
|
|
131
|
+
if self.clim[0] == self.clim[1]:
|
|
132
|
+
return self.clim[0], np.inf
|
|
133
|
+
# if the internalformat of the texture is normalized we need to
|
|
134
|
+
# also normalize the clims so they match in-shader
|
|
135
|
+
clim_min = self.normalize_value(self.clim[0], self._data_dtype)
|
|
136
|
+
clim_max = self.normalize_value(self.clim[1], self._data_dtype)
|
|
137
|
+
return clim_min, clim_max
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def is_normalized(self):
|
|
141
|
+
"""Whether the in-shader representation of this texture is normalized or not.
|
|
142
|
+
|
|
143
|
+
Formats ending in 'f' (float), 'ui' (unsigned integer), or 'i'
|
|
144
|
+
(signed integer) are not normalized in the GPU. Formats ending in "_snorm"
|
|
145
|
+
are normalized on the range [-1, 1] based on the data type of the
|
|
146
|
+
input data (ex. 0-255 for uint8). Formats with no data type suffix are
|
|
147
|
+
normalized on the range [0, 1]. See
|
|
148
|
+
https://www.khronos.org/opengl/wiki/Image_Format for more information.
|
|
149
|
+
|
|
150
|
+
This property can be used to determine if input shader variables
|
|
151
|
+
(uniforms, template variables) need to also be normalized. See
|
|
152
|
+
:meth:`~BaseTexture.normalize_value` below.
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
if self.internalformat is None:
|
|
156
|
+
return True
|
|
157
|
+
return self.internalformat[-1] not in ('f', 'i')
|
|
158
|
+
|
|
159
|
+
def normalize_value(self, val, input_data_dtype):
|
|
160
|
+
"""Normalize values to match in-shader representation of this shader.
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
val : int | float | ndarray
|
|
165
|
+
Value(s) to normalize.
|
|
166
|
+
input_data_dtype : numpy.dtype
|
|
167
|
+
Data type of input data. The assumption is that the provided
|
|
168
|
+
values to be normalized are in the same range as the input
|
|
169
|
+
texture data and must be normalized in the same way.
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
if not self.is_normalized:
|
|
173
|
+
return val
|
|
174
|
+
dtype_info = np.iinfo(input_data_dtype)
|
|
175
|
+
dmin = dtype_info.min
|
|
176
|
+
dmax = dtype_info.max
|
|
177
|
+
val = (val - dmin) / (dmax - dmin)
|
|
178
|
+
# XXX: Do we need to handle _snorm differently?
|
|
179
|
+
# Not currently supported in vispy.
|
|
180
|
+
return val
|
|
181
|
+
|
|
182
|
+
def _data_num_channels(self, data):
|
|
183
|
+
# if format == 'luminance':
|
|
184
|
+
# num_channels = 1
|
|
185
|
+
if data is not None:
|
|
186
|
+
# array or shape tuple
|
|
187
|
+
ndim = getattr(data, 'ndim', len(data))
|
|
188
|
+
# Ex. (M, N, 3) in Texture2D (ndim=2) -> 3 channels
|
|
189
|
+
num_channels = data.shape[-1] if ndim == self._ndim + 1 else 1
|
|
190
|
+
else:
|
|
191
|
+
num_channels = 4
|
|
192
|
+
return num_channels
|
|
193
|
+
|
|
194
|
+
def _create_rep_array(self, data):
|
|
195
|
+
"""Get a representative array with an initial shape.
|
|
196
|
+
|
|
197
|
+
Data will be filled in and the texture resized later.
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
dtype = getattr(data, 'dtype', np.float32)
|
|
201
|
+
num_channels = self._data_num_channels(data)
|
|
202
|
+
init_shape = (10,) * self._ndim + (num_channels,)
|
|
203
|
+
return np.zeros(init_shape).astype(dtype)
|
|
204
|
+
|
|
205
|
+
def check_data_format(self, data):
|
|
206
|
+
"""Check if provided data will cause issues if set later."""
|
|
207
|
+
# this texture type has no limitations
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
def scale_and_set_data(self, data, offset=None, copy=False):
|
|
211
|
+
"""Upload new data to the GPU."""
|
|
212
|
+
# we need to call super here or we get infinite recursion
|
|
213
|
+
return super().set_data(data, offset=offset, copy=copy)
|
|
214
|
+
|
|
215
|
+
def set_data(self, data, offset=None, copy=False):
|
|
216
|
+
self.scale_and_set_data(data, offset=offset, copy=copy)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class CPUScaledTextureMixin(_ScaledTextureMixin):
|
|
220
|
+
"""Texture mixin class for smarter scaling decisions.
|
|
221
|
+
|
|
222
|
+
This class wraps the logic to normalize data on the CPU before sending
|
|
223
|
+
it to the GPU (the texture). Pre-scaling on the CPU can be helpful in
|
|
224
|
+
cases where OpenGL 2/ES requirements limit the texture storage to an
|
|
225
|
+
8-bit normalized integer internally.
|
|
226
|
+
|
|
227
|
+
This class includes optimizations where image data is not re-normalized
|
|
228
|
+
if the previous normalization can still be used to visualize the data
|
|
229
|
+
with the new color limits.
|
|
230
|
+
|
|
231
|
+
This class should only be used internally. For similar features where
|
|
232
|
+
scaling occurs on the GPU see
|
|
233
|
+
:class:`vispy.visuals._scalable_textures.GPUScaledTextureMixin`.
|
|
234
|
+
|
|
235
|
+
To use this mixin, a subclass should be created to combine this mixin with
|
|
236
|
+
the texture class being used. Existing subclasses already exist in this
|
|
237
|
+
module. Note that this class **must** appear first in the subclass's parent
|
|
238
|
+
classes so that its ``__init__`` method is called instead of the parent
|
|
239
|
+
Texture class.
|
|
240
|
+
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
def __init__(self, data=None, **texture_kwargs):
|
|
244
|
+
self._data_limits = None
|
|
245
|
+
# Call the __init__ of the mixin base class
|
|
246
|
+
super().__init__(data, **texture_kwargs)
|
|
247
|
+
|
|
248
|
+
def _clim_outside_data_limits(self, cmin, cmax):
|
|
249
|
+
if self._data_limits is None:
|
|
250
|
+
return False
|
|
251
|
+
return cmin < self._data_limits[0] or cmax > self._data_limits[1]
|
|
252
|
+
|
|
253
|
+
def set_clim(self, clim):
|
|
254
|
+
"""Set clim and return if a texture update is needed."""
|
|
255
|
+
need_texture_upload = False
|
|
256
|
+
# NOTE: Color limits are not checked against data type limits
|
|
257
|
+
if isinstance(clim, str):
|
|
258
|
+
if clim != 'auto':
|
|
259
|
+
raise ValueError('clim must be "auto" if a string')
|
|
260
|
+
need_texture_upload = True
|
|
261
|
+
self._clim = clim
|
|
262
|
+
else:
|
|
263
|
+
try:
|
|
264
|
+
cmin, cmax = clim
|
|
265
|
+
except (ValueError, TypeError):
|
|
266
|
+
raise ValueError('clim must have two elements')
|
|
267
|
+
if self._clim_outside_data_limits(cmin, cmax):
|
|
268
|
+
need_texture_upload = True
|
|
269
|
+
self._clim = (cmin, cmax)
|
|
270
|
+
return need_texture_upload
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def clim_normalized(self):
|
|
274
|
+
"""Normalize current clims to match texture data inside the shader.
|
|
275
|
+
|
|
276
|
+
If data is scaled on the CPU then the texture data will be in the range
|
|
277
|
+
0-1 in the _build_texture() method. Inside the fragment shader the
|
|
278
|
+
final contrast adjustment will be applied based on this normalized
|
|
279
|
+
``clim``.
|
|
280
|
+
|
|
281
|
+
"""
|
|
282
|
+
if isinstance(self.clim, str) and self.clim == "auto":
|
|
283
|
+
raise RuntimeError("Can't return 'auto' normalized color limits "
|
|
284
|
+
"until data has been set. Call "
|
|
285
|
+
"'scale_and_set_data' first.")
|
|
286
|
+
if self._data_limits is None:
|
|
287
|
+
raise RuntimeError("Can't return normalized color limits until "
|
|
288
|
+
"data has been set. Call "
|
|
289
|
+
"'scale_and_set_data' first.")
|
|
290
|
+
|
|
291
|
+
range_min, range_max = self._data_limits
|
|
292
|
+
clim_min, clim_max = self.clim
|
|
293
|
+
if clim_min == clim_max:
|
|
294
|
+
return 0, np.inf
|
|
295
|
+
clim_min = (clim_min - range_min) / (range_max - range_min)
|
|
296
|
+
clim_max = (clim_max - range_min) / (range_max - range_min)
|
|
297
|
+
return clim_min, clim_max
|
|
298
|
+
|
|
299
|
+
@staticmethod
|
|
300
|
+
def _scale_data_on_cpu(data, clim, copy=True):
|
|
301
|
+
data = np.array(data, dtype=np.float32, copy=copy)
|
|
302
|
+
if clim[0] != clim[1]:
|
|
303
|
+
# we always must copy the data if we change it here, otherwise it might change
|
|
304
|
+
# unexpectedly the data held outside of here
|
|
305
|
+
if not copy:
|
|
306
|
+
data = data.copy()
|
|
307
|
+
data -= clim[0]
|
|
308
|
+
data *= 1 / (clim[1] - clim[0])
|
|
309
|
+
return data
|
|
310
|
+
|
|
311
|
+
def scale_and_set_data(self, data, offset=None, copy=True):
|
|
312
|
+
"""Upload new data to the GPU, scaling if necessary."""
|
|
313
|
+
if self._data_dtype is None:
|
|
314
|
+
data.dtype == self._data_dtype
|
|
315
|
+
|
|
316
|
+
# ensure dtype is the same as it was before, or funny things happen
|
|
317
|
+
# no copy is performed unless asked for or necessary
|
|
318
|
+
data = convert_dtype_and_clip(data, self._data_dtype, copy=copy)
|
|
319
|
+
|
|
320
|
+
clim = self._clim
|
|
321
|
+
is_auto = isinstance(clim, str) and clim == 'auto'
|
|
322
|
+
if data.ndim == self._ndim or data.shape[self._ndim] == 1:
|
|
323
|
+
if is_auto:
|
|
324
|
+
clim = get_default_clim_from_data(data)
|
|
325
|
+
data = self._scale_data_on_cpu(data, clim, copy=False)
|
|
326
|
+
data_limits = clim
|
|
327
|
+
else:
|
|
328
|
+
data_limits = get_default_clim_from_dtype(data.dtype)
|
|
329
|
+
if is_auto:
|
|
330
|
+
clim = data_limits
|
|
331
|
+
|
|
332
|
+
self._clim = float(clim[0]), float(clim[1])
|
|
333
|
+
self._data_limits = data_limits
|
|
334
|
+
return super().scale_and_set_data(data, offset=offset, copy=False)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class GPUScaledTextureMixin(_ScaledTextureMixin):
|
|
338
|
+
"""Texture class for smarter scaling and internalformat decisions.
|
|
339
|
+
|
|
340
|
+
This texture class uses internal formats that are not supported by
|
|
341
|
+
strict OpenGL 2/ES drivers without additional extensions. By using
|
|
342
|
+
this texture we upload data to the GPU in a format as close to
|
|
343
|
+
the original data type as possible (32-bit floats on the CPU are 32-bit
|
|
344
|
+
floats on the GPU). No normalization/scaling happens on the CPU and
|
|
345
|
+
all of it happens on the GPU. This should avoid unnecessary data copies
|
|
346
|
+
as well as provide the highest precision for the final visualization.
|
|
347
|
+
|
|
348
|
+
The texture format may either be a GL enum string (ex. 'r32f'), a numpy
|
|
349
|
+
dtype object (ex. np.float32), or 'auto' which means the texture will
|
|
350
|
+
try to pick the best format for the provided data. By using 'auto' you
|
|
351
|
+
also give the texture permission to change formats in the future if
|
|
352
|
+
new data is provided with a different data type.
|
|
353
|
+
|
|
354
|
+
This class should only be used internally. For similar features where
|
|
355
|
+
scaling occurs on the CPU see
|
|
356
|
+
:class:`vispy.visuals._scalable_textures.CPUScaledTextureMixin`.
|
|
357
|
+
|
|
358
|
+
To use this mixin, a subclass should be created to combine this mixin with
|
|
359
|
+
the texture class being used. Existing subclasses already exist in this
|
|
360
|
+
module. Note that this class **must** appear first in the subclass's parent
|
|
361
|
+
classes so that its ``__init__`` method is called instead of the parent
|
|
362
|
+
Texture class.
|
|
363
|
+
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
# dtype -> internalformat
|
|
367
|
+
# 'r' will be replaced (if needed) with rgb or rgba depending on number of bands
|
|
368
|
+
_texture_dtype_format = {
|
|
369
|
+
np.float32: 'r32f',
|
|
370
|
+
np.float64: 'r32f',
|
|
371
|
+
np.uint8: 'r8', # uint8 normalized
|
|
372
|
+
np.uint16: 'r16', # uint16 normalized
|
|
373
|
+
# np.int8: 'r8', # not supported, there are no signed-integer norm formats
|
|
374
|
+
# np.int16: 'r16',
|
|
375
|
+
# np.uint32: 'r32ui', # not supported, no normal formats for 32bit ints
|
|
376
|
+
# np.int32: 'r32i',
|
|
377
|
+
}
|
|
378
|
+
# instance variable that will be used later on
|
|
379
|
+
_auto_texture_format = False
|
|
380
|
+
|
|
381
|
+
def _handle_auto_texture_format(self, texture_format, data):
|
|
382
|
+
if isinstance(texture_format, str) and texture_format == 'auto':
|
|
383
|
+
if data is None:
|
|
384
|
+
warnings.warn("'texture_format' set to 'auto' but no data "
|
|
385
|
+
"provided. Falling back to CPU scaling.")
|
|
386
|
+
texture_format = None
|
|
387
|
+
else:
|
|
388
|
+
texture_format = data.dtype.type
|
|
389
|
+
self._auto_texture_format = True
|
|
390
|
+
return texture_format
|
|
391
|
+
|
|
392
|
+
def _get_gl_tex_format(self, texture_format, num_channels):
|
|
393
|
+
if texture_format and not isinstance(texture_format, str):
|
|
394
|
+
texture_format = np.dtype(texture_format).type
|
|
395
|
+
if texture_format not in self._texture_dtype_format:
|
|
396
|
+
raise ValueError("Can't determine internal texture format for '{}'".format(texture_format))
|
|
397
|
+
texture_format = self._texture_dtype_format[texture_format]
|
|
398
|
+
# adjust internalformat for format of data (RGBA vs L)
|
|
399
|
+
texture_format = texture_format.replace('r', 'rgba'[:num_channels])
|
|
400
|
+
return texture_format
|
|
401
|
+
|
|
402
|
+
def _get_texture_format_for_data(self, data, internalformat):
|
|
403
|
+
if internalformat is None:
|
|
404
|
+
raise ValueError("'internalformat' must be provided for GPU scaled textures.")
|
|
405
|
+
num_channels = self._data_num_channels(data)
|
|
406
|
+
texture_format = self._handle_auto_texture_format(internalformat, data)
|
|
407
|
+
texture_format = self._get_gl_tex_format(texture_format, num_channels)
|
|
408
|
+
return texture_format
|
|
409
|
+
|
|
410
|
+
def _compute_clim(self, data):
|
|
411
|
+
clim = self._clim
|
|
412
|
+
is_auto = isinstance(clim, str) and clim == 'auto'
|
|
413
|
+
if data.ndim == self._ndim or data.shape[2] == 1:
|
|
414
|
+
if is_auto:
|
|
415
|
+
clim = get_default_clim_from_data(data)
|
|
416
|
+
elif is_auto:
|
|
417
|
+
# assume that RGB data is already scaled (0, 1)
|
|
418
|
+
clim = get_default_clim_from_dtype(data.dtype)
|
|
419
|
+
return float(clim[0]), float(clim[1])
|
|
420
|
+
|
|
421
|
+
def _internalformat_will_change(self, data):
|
|
422
|
+
shape_repr = self._create_rep_array(data)
|
|
423
|
+
new_if = self._get_gl_tex_format(data.dtype, shape_repr.shape[-1])
|
|
424
|
+
return new_if != self.internalformat
|
|
425
|
+
|
|
426
|
+
def check_data_format(self, data):
|
|
427
|
+
"""Check if provided data will cause issues if set later."""
|
|
428
|
+
if self._internalformat_will_change(data) and not self._auto_texture_format:
|
|
429
|
+
raise ValueError("Data being set would cause a format change "
|
|
430
|
+
"in the texture. This is only allowed when "
|
|
431
|
+
"'texture_format' is set to 'auto'.")
|
|
432
|
+
|
|
433
|
+
def _reformat_if_necessary(self, data):
|
|
434
|
+
if not self._internalformat_will_change(data):
|
|
435
|
+
return
|
|
436
|
+
if self._auto_texture_format:
|
|
437
|
+
shape_repr = self._create_rep_array(data)
|
|
438
|
+
internalformat = self._get_gl_tex_format(data.dtype, shape_repr.shape[-1])
|
|
439
|
+
self._resize(data.shape, internalformat=internalformat)
|
|
440
|
+
else:
|
|
441
|
+
raise RuntimeError("'internalformat' needs to change but "
|
|
442
|
+
"'texture_format' was not 'auto'.")
|
|
443
|
+
|
|
444
|
+
def scale_and_set_data(self, data, offset=None, copy=False):
|
|
445
|
+
"""Upload new data to the GPU, scaling if necessary."""
|
|
446
|
+
self._reformat_if_necessary(data)
|
|
447
|
+
self._data_dtype = np.dtype(data.dtype)
|
|
448
|
+
self._clim = self._compute_clim(data)
|
|
449
|
+
return super().scale_and_set_data(data, offset=offset, copy=copy)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class CPUScaledTexture2D(CPUScaledTextureMixin, Texture2D):
|
|
453
|
+
"""Texture class with clim scaling handling builtin.
|
|
454
|
+
|
|
455
|
+
See :class:`vispy.visuals._scalable_textures.CPUScaledTextureMixin` for
|
|
456
|
+
more information.
|
|
457
|
+
|
|
458
|
+
"""
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
class GPUScaledTexture2D(GPUScaledTextureMixin, Texture2D):
|
|
462
|
+
"""Texture class with clim scaling handling builtin.
|
|
463
|
+
|
|
464
|
+
See :class:`vispy.visuals._scalable_textures.GPUScaledTextureMixin` for
|
|
465
|
+
more information.
|
|
466
|
+
|
|
467
|
+
"""
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
class CPUScaledTexture3D(CPUScaledTextureMixin, Texture3D):
|
|
471
|
+
"""Texture class with clim scaling handling builtin.
|
|
472
|
+
|
|
473
|
+
See :class:`vispy.visuals._scalable_textures.CPUScaledTextureMixin` for
|
|
474
|
+
more information.
|
|
475
|
+
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
class GPUScaledTextured3D(GPUScaledTextureMixin, Texture3D):
|
|
480
|
+
"""Texture class with clim scaling handling builtin.
|
|
481
|
+
|
|
482
|
+
See :class:`vispy.visuals._scalable_textures.GPUScaledTextureMixin` for
|
|
483
|
+
more information.
|
|
484
|
+
|
|
485
|
+
"""
|