vispy 0.14.0__cp311-cp311-macosx_11_0_arm64.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-311-darwin.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 +5 -0
- vispy-0.14.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,473 @@
|
|
|
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
|
+
"""Test utilities for comparing rendered results with expected image files.
|
|
7
|
+
|
|
8
|
+
Procedure for unit-testing with images:
|
|
9
|
+
|
|
10
|
+
1. Run unit tests at least once; this initializes a git clone of
|
|
11
|
+
vispy/test-data in config['test_data_path']. This path is
|
|
12
|
+
`~/.vispy/test-data` unless the config variable has been modified.
|
|
13
|
+
The config file is located at `vispy/vispy/util/config.py`
|
|
14
|
+
|
|
15
|
+
2. Run individual test scripts with the --vispy-audit flag:
|
|
16
|
+
|
|
17
|
+
$ python vispy/visuals/tests/test_ellipse.py --vispy-audit
|
|
18
|
+
|
|
19
|
+
Any failing tests will
|
|
20
|
+
display the test results, standard image, and the differences between the
|
|
21
|
+
two. If the test result is bad, then press (f)ail. If the test result is
|
|
22
|
+
good, then press (p)ass and the new image will be saved to the test-data
|
|
23
|
+
directory.
|
|
24
|
+
|
|
25
|
+
3. After adding or changing test images, create a new commit:
|
|
26
|
+
|
|
27
|
+
$ cd ~/.vispy/test-data
|
|
28
|
+
$ git add ...
|
|
29
|
+
$ git commit -a
|
|
30
|
+
|
|
31
|
+
4. Look up the most recent tag name from the `test_data_tag` variable in
|
|
32
|
+
get_test_data_repo() below. Increment the tag name by 1 in the function
|
|
33
|
+
and create a new tag in the test-data repository:
|
|
34
|
+
|
|
35
|
+
$ git tag test-data-NNN
|
|
36
|
+
$ git push --tags origin main
|
|
37
|
+
|
|
38
|
+
This tag is used to ensure that each vispy commit is linked to a specific
|
|
39
|
+
commit in the test-data repository. This makes it possible to push new
|
|
40
|
+
commits to the test-data repository without interfering with existing
|
|
41
|
+
tests, and also allows unit tests to continue working on older vispy
|
|
42
|
+
versions.
|
|
43
|
+
|
|
44
|
+
Finally, update the tag name in ``get_test_data_repo`` to the new name.
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
import time
|
|
49
|
+
import os
|
|
50
|
+
import sys
|
|
51
|
+
import inspect
|
|
52
|
+
import base64
|
|
53
|
+
from subprocess import check_call, CalledProcessError
|
|
54
|
+
import numpy as np
|
|
55
|
+
|
|
56
|
+
from http.client import HTTPConnection
|
|
57
|
+
from urllib.parse import urlencode
|
|
58
|
+
|
|
59
|
+
from .. import scene, config
|
|
60
|
+
from ..io import read_png, write_png
|
|
61
|
+
from ..gloo.util import _screenshot
|
|
62
|
+
from ..util import run_subprocess
|
|
63
|
+
from . import IS_CI
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
tester = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _get_tester():
|
|
70
|
+
global tester
|
|
71
|
+
if tester is None:
|
|
72
|
+
tester = ImageTester()
|
|
73
|
+
return tester
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def assert_image_approved(image, standard_file, message=None, **kwargs):
|
|
77
|
+
"""Check that an image test result matches a pre-approved standard.
|
|
78
|
+
|
|
79
|
+
If the result does not match, then the user can optionally invoke a GUI
|
|
80
|
+
to compare the images and decide whether to fail the test or save the new
|
|
81
|
+
image as the standard.
|
|
82
|
+
|
|
83
|
+
This function will automatically clone the test-data repository into
|
|
84
|
+
~/.vispy/test-data. However, it is up to the user to ensure this repository
|
|
85
|
+
is kept up to date and to commit/push new images after they are saved.
|
|
86
|
+
|
|
87
|
+
Run the test with python <test-path> --vispy-audit-tests to bring up
|
|
88
|
+
the auditing GUI.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
image : (h, w, 4) ndarray or 'screenshot'
|
|
93
|
+
The test result to check
|
|
94
|
+
standard_file : str
|
|
95
|
+
The name of the approved test image to check against. This file name
|
|
96
|
+
is relative to the root of the vispy test-data repository and will
|
|
97
|
+
be automatically fetched.
|
|
98
|
+
message : str
|
|
99
|
+
A string description of the image. It is recommended to describe
|
|
100
|
+
specific features that an auditor should look for when deciding whether
|
|
101
|
+
to fail a test.
|
|
102
|
+
|
|
103
|
+
Extra keyword arguments are used to set the thresholds for automatic image
|
|
104
|
+
comparison (see ``assert_image_match()``).
|
|
105
|
+
"""
|
|
106
|
+
if isinstance(image, str) and image == "screenshot":
|
|
107
|
+
image = _screenshot(alpha=True)
|
|
108
|
+
if message is None:
|
|
109
|
+
code = inspect.currentframe().f_back.f_code
|
|
110
|
+
message = "%s::%s" % (code.co_filename, code.co_name)
|
|
111
|
+
|
|
112
|
+
# Make sure we have a test data repo available, possibly invoking git
|
|
113
|
+
data_path = get_test_data_repo()
|
|
114
|
+
|
|
115
|
+
# Read the standard image if it exists
|
|
116
|
+
std_file = os.path.join(data_path, standard_file)
|
|
117
|
+
if not os.path.isfile(std_file):
|
|
118
|
+
std_image = None
|
|
119
|
+
else:
|
|
120
|
+
std_image = read_png(std_file)
|
|
121
|
+
|
|
122
|
+
# If the test image does not match, then we go to audit if requested.
|
|
123
|
+
try:
|
|
124
|
+
if image.shape != std_image.shape:
|
|
125
|
+
# Allow im1 to be an integer multiple larger than im2 to account
|
|
126
|
+
# for high-resolution displays
|
|
127
|
+
ims1 = np.array(image.shape).astype(float)
|
|
128
|
+
ims2 = np.array(std_image.shape).astype(float)
|
|
129
|
+
sr = ims1 / ims2
|
|
130
|
+
if (sr[0] != sr[1] or not np.allclose(sr, np.round(sr)) or
|
|
131
|
+
sr[0] < 1):
|
|
132
|
+
raise TypeError("Test result shape %s is not an integer factor"
|
|
133
|
+
" larger than standard image shape %s." %
|
|
134
|
+
(ims1, ims2))
|
|
135
|
+
sr = np.round(sr).astype(int)
|
|
136
|
+
image = downsample(image, sr[0], axis=(0, 1)).astype(image.dtype)
|
|
137
|
+
|
|
138
|
+
assert_image_match(image, std_image, **kwargs)
|
|
139
|
+
except Exception:
|
|
140
|
+
if standard_file in git_status(data_path):
|
|
141
|
+
print("\n\nWARNING: unit test failed against modified standard "
|
|
142
|
+
"image %s.\nTo revert this file, run `cd %s; git checkout "
|
|
143
|
+
"%s`\n" % (std_file, data_path, standard_file))
|
|
144
|
+
if config['audit_tests']:
|
|
145
|
+
sys.excepthook(*sys.exc_info())
|
|
146
|
+
_get_tester().test(image, std_image, message)
|
|
147
|
+
std_path = os.path.dirname(std_file)
|
|
148
|
+
print('Saving new standard image to "%s"' % std_file)
|
|
149
|
+
if not os.path.isdir(std_path):
|
|
150
|
+
os.makedirs(std_path)
|
|
151
|
+
write_png(std_file, image)
|
|
152
|
+
else:
|
|
153
|
+
if std_image is None:
|
|
154
|
+
raise Exception("Test standard %s does not exist." % std_file)
|
|
155
|
+
else:
|
|
156
|
+
if IS_CI:
|
|
157
|
+
_save_failed_test(image, std_image, standard_file)
|
|
158
|
+
raise
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def assert_image_match(im1, im2, min_corr=0.9, px_threshold=50.,
|
|
162
|
+
px_count=None, max_px_diff=None, avg_px_diff=None,
|
|
163
|
+
img_diff=None):
|
|
164
|
+
"""Check that two images match.
|
|
165
|
+
|
|
166
|
+
Images that differ in shape or dtype will fail unconditionally.
|
|
167
|
+
Further tests for similarity depend on the arguments supplied.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
im1 : (h, w, 4) ndarray
|
|
172
|
+
Test output image
|
|
173
|
+
im2 : (h, w, 4) ndarray
|
|
174
|
+
Test standard image
|
|
175
|
+
min_corr : float or None
|
|
176
|
+
Minimum allowed correlation coefficient between corresponding image
|
|
177
|
+
values (see numpy.corrcoef)
|
|
178
|
+
px_threshold : float
|
|
179
|
+
Minimum value difference at which two pixels are considered different
|
|
180
|
+
px_count : int or None
|
|
181
|
+
Maximum number of pixels that may differ
|
|
182
|
+
max_px_diff : float or None
|
|
183
|
+
Maximum allowed difference between pixels
|
|
184
|
+
avg_px_diff : float or None
|
|
185
|
+
Average allowed difference between pixels
|
|
186
|
+
img_diff : float or None
|
|
187
|
+
Maximum allowed summed difference between images
|
|
188
|
+
|
|
189
|
+
"""
|
|
190
|
+
assert im1.ndim == 3
|
|
191
|
+
assert im1.shape[2] == 4
|
|
192
|
+
assert im1.dtype == im2.dtype
|
|
193
|
+
|
|
194
|
+
diff = im1.astype(float) - im2.astype(float)
|
|
195
|
+
if img_diff is not None:
|
|
196
|
+
assert np.abs(diff).sum() <= img_diff
|
|
197
|
+
|
|
198
|
+
pxdiff = diff.max(axis=2) # largest value difference per pixel
|
|
199
|
+
mask = np.abs(pxdiff) >= px_threshold
|
|
200
|
+
if px_count is not None:
|
|
201
|
+
assert mask.sum() <= px_count
|
|
202
|
+
|
|
203
|
+
masked_diff = diff[mask]
|
|
204
|
+
if max_px_diff is not None and masked_diff.size > 0:
|
|
205
|
+
assert masked_diff.max() <= max_px_diff
|
|
206
|
+
if avg_px_diff is not None and masked_diff.size > 0:
|
|
207
|
+
assert masked_diff.mean() <= avg_px_diff
|
|
208
|
+
|
|
209
|
+
if min_corr is not None:
|
|
210
|
+
with np.errstate(invalid='ignore'):
|
|
211
|
+
corr = np.corrcoef(im1.ravel(), im2.ravel())[0, 1]
|
|
212
|
+
assert corr >= min_corr
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _save_failed_test(data, expect, filename):
|
|
216
|
+
from ..io import _make_png
|
|
217
|
+
commit, error = run_subprocess(['git', 'rev-parse', 'HEAD'])
|
|
218
|
+
name = filename.split('/')
|
|
219
|
+
name.insert(-1, commit.strip())
|
|
220
|
+
filename = '/'.join(name)
|
|
221
|
+
host = 'data.vispy.org'
|
|
222
|
+
|
|
223
|
+
# concatenate data, expect, and diff into a single image
|
|
224
|
+
ds = data.shape
|
|
225
|
+
es = expect.shape
|
|
226
|
+
|
|
227
|
+
shape = (max(ds[0], es[0]) + 4, ds[1] + es[1] + 8 + max(ds[1], es[1]), 4)
|
|
228
|
+
img = np.empty(shape, dtype=np.ubyte)
|
|
229
|
+
img[..., :3] = 100
|
|
230
|
+
img[..., 3] = 255
|
|
231
|
+
|
|
232
|
+
img[2:2+ds[0], 2:2+ds[1], :ds[2]] = data
|
|
233
|
+
img[2:2+es[0], ds[1]+4:ds[1]+4+es[1], :es[2]] = expect
|
|
234
|
+
|
|
235
|
+
diff = make_diff_image(data, expect)
|
|
236
|
+
img[2:2+diff.shape[0], -diff.shape[1]-2:-2] = diff
|
|
237
|
+
|
|
238
|
+
png = _make_png(img)
|
|
239
|
+
conn = HTTPConnection(host)
|
|
240
|
+
req = urlencode({'name': filename,
|
|
241
|
+
'data': base64.b64encode(png)})
|
|
242
|
+
conn.request('POST', '/upload.py', req)
|
|
243
|
+
response = conn.getresponse().read()
|
|
244
|
+
conn.close()
|
|
245
|
+
print("\nImage comparison failed. Test result: %s %s Expected result: "
|
|
246
|
+
"%s %s" % (data.shape, data.dtype, expect.shape, expect.dtype))
|
|
247
|
+
print("Uploaded to: \nhttp://%s/data/%s" % (host, filename))
|
|
248
|
+
if not response.startswith(b'OK'):
|
|
249
|
+
print("WARNING: Error uploading data to %s" % host)
|
|
250
|
+
print(response)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def make_diff_image(im1, im2):
|
|
254
|
+
"""Return image array showing the differences between im1 and im2.
|
|
255
|
+
|
|
256
|
+
Handles images of different shape. Alpha channels are not compared.
|
|
257
|
+
"""
|
|
258
|
+
ds = im1.shape
|
|
259
|
+
es = im2.shape
|
|
260
|
+
|
|
261
|
+
diff = np.empty((max(ds[0], es[0]), max(ds[1], es[1]), 4), dtype=int)
|
|
262
|
+
diff[..., :3] = 128
|
|
263
|
+
diff[..., 3] = 255
|
|
264
|
+
diff[:ds[0], :ds[1], :min(ds[2], 3)] += im1[..., :3]
|
|
265
|
+
diff[:es[0], :es[1], :min(es[2], 3)] -= im2[..., :3]
|
|
266
|
+
diff = np.clip(diff, 0, 255).astype(np.ubyte)
|
|
267
|
+
return diff
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def downsample(data, n, axis=0):
|
|
271
|
+
"""Downsample by averaging points together across axis.
|
|
272
|
+
If multiple axes are specified, runs once per axis.
|
|
273
|
+
"""
|
|
274
|
+
if hasattr(axis, '__len__'):
|
|
275
|
+
if not hasattr(n, '__len__'):
|
|
276
|
+
n = [n]*len(axis)
|
|
277
|
+
for i in range(len(axis)):
|
|
278
|
+
data = downsample(data, n[i], axis[i])
|
|
279
|
+
return data
|
|
280
|
+
|
|
281
|
+
if n <= 1:
|
|
282
|
+
return data
|
|
283
|
+
nPts = int(data.shape[axis] / n)
|
|
284
|
+
s = list(data.shape)
|
|
285
|
+
s[axis] = nPts
|
|
286
|
+
s.insert(axis+1, n)
|
|
287
|
+
sl = [slice(None)] * data.ndim
|
|
288
|
+
sl[axis] = slice(0, nPts*n)
|
|
289
|
+
d1 = data[tuple(sl)]
|
|
290
|
+
d1.shape = tuple(s)
|
|
291
|
+
d2 = d1.mean(axis+1)
|
|
292
|
+
|
|
293
|
+
return d2
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class ImageTester(scene.SceneCanvas):
|
|
297
|
+
"""Graphical interface for auditing image comparison tests."""
|
|
298
|
+
|
|
299
|
+
def __init__(self):
|
|
300
|
+
self.grid = None
|
|
301
|
+
self.views = None
|
|
302
|
+
self.console = None
|
|
303
|
+
self.last_key = None
|
|
304
|
+
|
|
305
|
+
scene.SceneCanvas.__init__(self, size=(1000, 800))
|
|
306
|
+
|
|
307
|
+
self.bgcolor = (0.1, 0.1, 0.1, 1)
|
|
308
|
+
self.grid = self.central_widget.add_grid()
|
|
309
|
+
border = (0.3, 0.3, 0.3, 1)
|
|
310
|
+
self.views = (self.grid.add_view(row=0, col=0, border_color=border),
|
|
311
|
+
self.grid.add_view(row=0, col=1, border_color=border),
|
|
312
|
+
self.grid.add_view(row=0, col=2, border_color=border))
|
|
313
|
+
label_text = ['test output', 'standard', 'diff']
|
|
314
|
+
for i, v in enumerate(self.views):
|
|
315
|
+
v.camera = 'panzoom'
|
|
316
|
+
v.camera.aspect = 1
|
|
317
|
+
v.camera.flip = (False, True)
|
|
318
|
+
# unfreeze it to set the image and label on the view
|
|
319
|
+
# this is slightly hacky, but it is simpler than
|
|
320
|
+
# creating another class/storing as a dict or a tuple
|
|
321
|
+
v.unfreeze()
|
|
322
|
+
v.image = scene.Image(parent=v.scene)
|
|
323
|
+
v.label = scene.Text(label_text[i], parent=v, color='yellow',
|
|
324
|
+
anchor_x='left', anchor_y='top')
|
|
325
|
+
v.freeze()
|
|
326
|
+
|
|
327
|
+
self.views[1].camera.link(self.views[0].camera)
|
|
328
|
+
self.views[2].camera.link(self.views[0].camera)
|
|
329
|
+
self.console = scene.Console(text_color='white', border_color=border)
|
|
330
|
+
self.grid.add_widget(self.console, row=1, col=0, col_span=3)
|
|
331
|
+
|
|
332
|
+
def test(self, im1, im2, message):
|
|
333
|
+
self.show()
|
|
334
|
+
self.console.write('------------------')
|
|
335
|
+
self.console.write(message)
|
|
336
|
+
if im2 is None:
|
|
337
|
+
self.console.write('Image1: %s %s Image2: [no standard]' %
|
|
338
|
+
(im1.shape, im1.dtype))
|
|
339
|
+
im2 = np.zeros((1, 1, 3), dtype=np.ubyte)
|
|
340
|
+
else:
|
|
341
|
+
self.console.write('Image1: %s %s Image2: %s %s' %
|
|
342
|
+
(im1.shape, im1.dtype, im2.shape, im2.dtype))
|
|
343
|
+
self.console.write('(P)ass or (F)ail this test?')
|
|
344
|
+
self.views[0].image.set_data(im1)
|
|
345
|
+
self.views[1].image.set_data(im2)
|
|
346
|
+
diff = make_diff_image(im1, im2)
|
|
347
|
+
|
|
348
|
+
self.views[2].image.set_data(diff)
|
|
349
|
+
self.views[0].camera.set_range()
|
|
350
|
+
|
|
351
|
+
while True:
|
|
352
|
+
self.app.process_events()
|
|
353
|
+
if self.last_key is None:
|
|
354
|
+
pass
|
|
355
|
+
elif self.last_key.lower() == 'p':
|
|
356
|
+
self.console.write('PASS')
|
|
357
|
+
break
|
|
358
|
+
elif self.last_key.lower() in ('f', 'esc'):
|
|
359
|
+
self.console.write('FAIL')
|
|
360
|
+
raise Exception("User rejected test result.")
|
|
361
|
+
time.sleep(0.03)
|
|
362
|
+
|
|
363
|
+
for v in self.views:
|
|
364
|
+
v.image.set_data(np.zeros((1, 1, 3), dtype=np.ubyte))
|
|
365
|
+
|
|
366
|
+
def on_key_press(self, event):
|
|
367
|
+
self.last_key = event.key.name
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def get_test_data_repo():
|
|
371
|
+
"""Return the path to a git repository with the required commit checked
|
|
372
|
+
out.
|
|
373
|
+
|
|
374
|
+
If the repository does not exist, then it is cloned from
|
|
375
|
+
https://github.com/vispy/test-data. If the repository already exists
|
|
376
|
+
then the required commit is checked out.
|
|
377
|
+
"""
|
|
378
|
+
# This tag marks the test-data commit that this version of vispy should
|
|
379
|
+
# be tested against. When adding or changing test images, create
|
|
380
|
+
# and push a new tag and update this variable.
|
|
381
|
+
test_data_tag = 'test-data-10'
|
|
382
|
+
|
|
383
|
+
data_path = config['test_data_path']
|
|
384
|
+
git_path = 'http://github.com/vispy/test-data'
|
|
385
|
+
gitbase = git_cmd_base(data_path)
|
|
386
|
+
|
|
387
|
+
if os.path.isdir(data_path):
|
|
388
|
+
# Already have a test-data repository to work with.
|
|
389
|
+
|
|
390
|
+
# Get the commit ID of test_data_tag. Do a fetch if necessary.
|
|
391
|
+
try:
|
|
392
|
+
tag_commit = git_commit_id(data_path, test_data_tag)
|
|
393
|
+
except NameError:
|
|
394
|
+
cmd = gitbase + ['fetch', '--tags', 'origin']
|
|
395
|
+
print(' '.join(cmd))
|
|
396
|
+
check_call(cmd)
|
|
397
|
+
try:
|
|
398
|
+
tag_commit = git_commit_id(data_path, test_data_tag)
|
|
399
|
+
except NameError:
|
|
400
|
+
raise Exception("Could not find tag '%s' in test-data repo at"
|
|
401
|
+
" %s" % (test_data_tag, data_path))
|
|
402
|
+
except Exception:
|
|
403
|
+
if not os.path.exists(os.path.join(data_path, '.git')):
|
|
404
|
+
raise Exception("Directory '%s' does not appear to be a git "
|
|
405
|
+
"repository. Please remove this directory." %
|
|
406
|
+
data_path)
|
|
407
|
+
else:
|
|
408
|
+
raise
|
|
409
|
+
|
|
410
|
+
# If HEAD is not the correct commit, then do a checkout
|
|
411
|
+
if git_commit_id(data_path, 'HEAD') != tag_commit:
|
|
412
|
+
print("Checking out test-data tag '%s'" % test_data_tag)
|
|
413
|
+
check_call(gitbase + ['checkout', test_data_tag])
|
|
414
|
+
|
|
415
|
+
else:
|
|
416
|
+
print("Attempting to create git clone of test data repo in %s.." %
|
|
417
|
+
data_path)
|
|
418
|
+
|
|
419
|
+
parent_path = os.path.split(data_path)[0]
|
|
420
|
+
if not os.path.isdir(parent_path):
|
|
421
|
+
os.makedirs(parent_path)
|
|
422
|
+
|
|
423
|
+
if IS_CI:
|
|
424
|
+
# Create a shallow clone of the test-data repository (to avoid
|
|
425
|
+
# downloading more data than is necessary)
|
|
426
|
+
os.makedirs(data_path)
|
|
427
|
+
cmds = [
|
|
428
|
+
gitbase + ['init'],
|
|
429
|
+
gitbase + ['remote', 'add', 'origin', git_path],
|
|
430
|
+
gitbase + ['fetch', '--tags', 'origin', test_data_tag,
|
|
431
|
+
'--depth=1'],
|
|
432
|
+
gitbase + ['checkout', '-b', 'main', 'FETCH_HEAD'],
|
|
433
|
+
]
|
|
434
|
+
else:
|
|
435
|
+
# Create a full clone
|
|
436
|
+
cmds = [['git', 'clone', git_path, data_path]]
|
|
437
|
+
|
|
438
|
+
for cmd in cmds:
|
|
439
|
+
print(' '.join(cmd))
|
|
440
|
+
rval = check_call(cmd)
|
|
441
|
+
if rval == 0:
|
|
442
|
+
continue
|
|
443
|
+
raise RuntimeError("Test data path '%s' does not exist and could "
|
|
444
|
+
"not be created with git. Either create a git "
|
|
445
|
+
"clone of %s or set the test_data_path "
|
|
446
|
+
"variable to an existing clone." %
|
|
447
|
+
(data_path, git_path))
|
|
448
|
+
|
|
449
|
+
return data_path
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def git_cmd_base(path):
|
|
453
|
+
return ['git', '--git-dir=%s/.git' % path, '--work-tree=%s' % path]
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def git_status(path):
|
|
457
|
+
"""Return a string listing all changes to the working tree in a git
|
|
458
|
+
repository.
|
|
459
|
+
"""
|
|
460
|
+
cmd = git_cmd_base(path) + ['status', '--porcelain']
|
|
461
|
+
return run_subprocess(cmd, stderr=None, universal_newlines=True)[0]
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def git_commit_id(path, ref):
|
|
465
|
+
"""Return the commit id of *ref* in the git repository at *path*."""
|
|
466
|
+
cmd = git_cmd_base(path) + ['show', ref]
|
|
467
|
+
try:
|
|
468
|
+
output = run_subprocess(cmd, stderr=None, universal_newlines=True)[0]
|
|
469
|
+
except CalledProcessError:
|
|
470
|
+
raise NameError("Unknown git reference '%s'" % ref)
|
|
471
|
+
commit = output.split('\n')[0]
|
|
472
|
+
assert commit[:7] == 'commit '
|
|
473
|
+
return commit[7:]
|
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
"""Test utilities for comparing rendered arrays with expected array results."""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, Any
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from numpy.typing import ArrayLike, DtypeLike
|
|
14
|
+
except ImportError:
|
|
15
|
+
ArrayLike = np.ndarray
|
|
16
|
+
DtypeLike = Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def compare_render(
|
|
20
|
+
orig_data: ArrayLike,
|
|
21
|
+
rendered_data: ArrayLike,
|
|
22
|
+
previous_render: Optional[ArrayLike] = None,
|
|
23
|
+
atol: Optional[float] = 1.0
|
|
24
|
+
):
|
|
25
|
+
"""Compare an expected original array with the rendered result.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
orig_data
|
|
30
|
+
Expected output result array. This will be converted to an RGBA array
|
|
31
|
+
to be compared against the rendered data.
|
|
32
|
+
rendered_data
|
|
33
|
+
Actual rendered result as an RGBA 8-bit unsigned array.
|
|
34
|
+
previous_render
|
|
35
|
+
Previous instance of a render that the current render should not be
|
|
36
|
+
equal to.
|
|
37
|
+
atol
|
|
38
|
+
Absolute tolerance to be passed to
|
|
39
|
+
:func:`numpy.testing.assert_allclose`.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
predicted = make_rgba(orig_data)
|
|
43
|
+
np.testing.assert_allclose(rendered_data.astype(float), predicted.astype(float), atol=atol)
|
|
44
|
+
if previous_render is not None:
|
|
45
|
+
# assert not allclose
|
|
46
|
+
pytest.raises(AssertionError, np.testing.assert_allclose,
|
|
47
|
+
rendered_data, previous_render, atol=10)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def max_for_dtype(input_dtype: DtypeLike):
|
|
51
|
+
"""Get maximum value an image array should have for a specific dtype.
|
|
52
|
+
|
|
53
|
+
This is max int for each integer type or 1.0 for floating point types.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
if np.issubdtype(input_dtype, np.integer):
|
|
57
|
+
max_val = np.iinfo(input_dtype).max
|
|
58
|
+
else:
|
|
59
|
+
max_val = 1.0
|
|
60
|
+
return max_val
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def make_rgba(data_in: ArrayLike) -> ArrayLike:
|
|
64
|
+
"""Convert any array to an RGBA array.
|
|
65
|
+
|
|
66
|
+
RGBA arrays have 3 dimensions where the last represents the channels. If
|
|
67
|
+
an Alpha channel needs to be added it will be made completely opaque.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
3D RGBA unsigned 8-bit array
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
max_val = max_for_dtype(data_in.dtype)
|
|
75
|
+
if data_in.ndim == 3 and data_in.shape[-1] == 1:
|
|
76
|
+
data_in = data_in.squeeze()
|
|
77
|
+
|
|
78
|
+
if data_in.ndim == 2:
|
|
79
|
+
out = np.stack([data_in] * 4, axis=2)
|
|
80
|
+
out[:, :, 3] = max_val
|
|
81
|
+
elif data_in.shape[-1] == 3:
|
|
82
|
+
out = np.concatenate((data_in, np.ones((*data_in.shape[:2], 1)) * max_val), axis=2)
|
|
83
|
+
else:
|
|
84
|
+
out = data_in
|
|
85
|
+
return np.round((out.astype(np.float32) * 255 / max_val)).astype(np.uint8)
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
from vispy.testing import (assert_in, assert_not_in, assert_is,
|
|
7
|
+
run_tests_if_main, assert_raises)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_testing():
|
|
11
|
+
"""Test testing ports"""
|
|
12
|
+
assert_raises(AssertionError, assert_in, 'foo', 'bar')
|
|
13
|
+
assert_in('foo', 'foobar')
|
|
14
|
+
assert_raises(AssertionError, assert_not_in, 'foo', 'foobar')
|
|
15
|
+
assert_not_in('foo', 'bar')
|
|
16
|
+
assert_raises(AssertionError, assert_is, None, 0)
|
|
17
|
+
assert_is(None, None)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
run_tests_if_main()
|
vispy/util/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
"""Utilities for Vispy. A collection of modules that are used in
|
|
6
|
+
one or more Vispy sub-packages.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .logs import logger, set_log_level, use_log_level # noqa
|
|
10
|
+
from .config import (config, sys_info, save_config, get_config_keys, # noqa
|
|
11
|
+
set_data_dir, _TempDir, _get_args) # noqa
|
|
12
|
+
from .fetching import load_data_file # noqa
|
|
13
|
+
from .frozen import Frozen # noqa
|
|
14
|
+
from . import fonts # noqa
|
|
15
|
+
from . import transforms # noqa
|
|
16
|
+
from .wrappers import use, run_subprocess # noqa
|
|
17
|
+
from .bunch import SimpleBunch # noqa
|
vispy/util/bunch.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
|
|
6
|
+
# Class adapted from mne-python
|
|
7
|
+
|
|
8
|
+
class SimpleBunch(dict):
|
|
9
|
+
"""Container object for datasets: dictionnary-like object that
|
|
10
|
+
exposes its keys as attributes.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, **kwargs):
|
|
14
|
+
dict.__init__(self, kwargs)
|
|
15
|
+
self.__dict__ = self
|
|
@@ -0,0 +1,57 @@
|
|
|
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 os
|
|
5
|
+
from packaging.version import Version
|
|
6
|
+
|
|
7
|
+
from vispy.util import use_log_level
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def has_matplotlib(version='1.2'):
|
|
11
|
+
"""Determine if mpl is a usable version"""
|
|
12
|
+
try:
|
|
13
|
+
import matplotlib
|
|
14
|
+
except Exception:
|
|
15
|
+
has_mpl = False
|
|
16
|
+
else:
|
|
17
|
+
if Version(matplotlib.__version__) >= Version(version):
|
|
18
|
+
has_mpl = True
|
|
19
|
+
else:
|
|
20
|
+
has_mpl = False
|
|
21
|
+
return has_mpl
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def has_skimage(version='0.11'):
|
|
25
|
+
"""Determine if scikit-image is a usable version"""
|
|
26
|
+
try:
|
|
27
|
+
import skimage
|
|
28
|
+
except ImportError:
|
|
29
|
+
return False
|
|
30
|
+
sk_version = Version(skimage.__version__)
|
|
31
|
+
return sk_version >= Version(version)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def has_backend(backend, has=(), capable=(), out=()):
|
|
35
|
+
from ..app.backends import BACKENDMAP
|
|
36
|
+
using = os.getenv('_VISPY_TESTING_APP', None)
|
|
37
|
+
if using is not None and using != backend:
|
|
38
|
+
# e.g., we are on a 'pyglet' run but the test requires PyQt4
|
|
39
|
+
ret = (False,) if len(out) > 0 else False
|
|
40
|
+
for o in out:
|
|
41
|
+
ret += (None,)
|
|
42
|
+
return ret
|
|
43
|
+
|
|
44
|
+
# let's follow the standard code path
|
|
45
|
+
module_name = BACKENDMAP[backend.lower()][1]
|
|
46
|
+
with use_log_level('warning', print_msg=False):
|
|
47
|
+
mod = __import__('app.backends.%s' % module_name, globals(), level=2)
|
|
48
|
+
mod = getattr(mod.backends, module_name)
|
|
49
|
+
good = mod.testable
|
|
50
|
+
for h in has:
|
|
51
|
+
good = (good and getattr(mod, 'has_%s' % h))
|
|
52
|
+
for cap in capable:
|
|
53
|
+
good = (good and mod.capability[cap])
|
|
54
|
+
ret = (good,) if len(out) > 0 else good
|
|
55
|
+
for o in out:
|
|
56
|
+
ret += (getattr(mod, o),)
|
|
57
|
+
return ret
|