vispy 0.15.0__cp313-cp313-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 +1003 -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 +1213 -0
- vispy/color/tests/__init__.py +0 -0
- vispy/color/tests/test_color.py +378 -0
- vispy/conftest.py +12 -0
- vispy/ext/__init__.py +0 -0
- vispy/ext/cocoapy.py +1522 -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 +162 -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 +700 -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 +594 -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 +568 -0
- vispy/gloo/gl/tests/test_names.py +246 -0
- vispy/gloo/gl/tests/test_use.py +71 -0
- vispy/gloo/glir.py +1824 -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 +1046 -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 +105 -0
- vispy/scene/cameras/base_camera.py +551 -0
- vispy/scene/cameras/fly.py +474 -0
- vispy/scene/cameras/magnify.py +163 -0
- vispy/scene/cameras/panzoom.py +311 -0
- vispy/scene/cameras/perspective.py +338 -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 +183 -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 +448 -0
- vispy/testing/_testing.py +416 -0
- vispy/testing/image_tester.py +494 -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 +32 -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 +21 -0
- vispy/visuals/__init__.py +50 -0
- vispy/visuals/_scalable_textures.py +487 -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 +162 -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 +801 -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 +161 -0
- vispy/visuals/gridmesh.py +98 -0
- vispy/visuals/histogram.py +58 -0
- vispy/visuals/image.py +701 -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 +819 -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_gridlines.py +30 -0
- vispy/visuals/tests/test_histogram.py +24 -0
- vispy/visuals/tests/test_image.py +392 -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 +196 -0
- vispy/visuals/tests/test_sdf.py +73 -0
- vispy/visuals/tests/test_spectrogram.py +42 -0
- vispy/visuals/tests/test_surface_plot.py +57 -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-313-darwin.so +0 -0
- vispy/visuals/text/_sdf_cpu.pyx +112 -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 +1366 -0
- vispy/visuals/windbarb.py +291 -0
- vispy/visuals/xyz_axis.py +34 -0
- vispy-0.15.0.dist-info/METADATA +243 -0
- vispy-0.15.0.dist-info/RECORD +521 -0
- vispy-0.15.0.dist-info/WHEEL +6 -0
- vispy-0.15.0.dist-info/licenses/LICENSE.txt +36 -0
- vispy-0.15.0.dist-info/top_level.txt +1 -0
vispy/scene/canvas.py
ADDED
|
@@ -0,0 +1,639 @@
|
|
|
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
|
+
from __future__ import division
|
|
6
|
+
|
|
7
|
+
import weakref
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from .. import gloo
|
|
11
|
+
from .. import app
|
|
12
|
+
from .visuals import VisualNode
|
|
13
|
+
from ..visuals.transforms import TransformSystem
|
|
14
|
+
from ..color import Color
|
|
15
|
+
from ..util import logger, Frozen
|
|
16
|
+
from ..util.profiler import Profiler
|
|
17
|
+
from .subscene import SubScene
|
|
18
|
+
from .events import SceneMouseEvent
|
|
19
|
+
from .widgets import Widget
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SceneCanvas(app.Canvas, Frozen):
|
|
23
|
+
"""A Canvas that automatically draws the contents of a scene
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
title : str
|
|
28
|
+
The widget title
|
|
29
|
+
size : (width, height)
|
|
30
|
+
The size of the window.
|
|
31
|
+
position : (x, y)
|
|
32
|
+
The position of the window in screen coordinates.
|
|
33
|
+
show : bool
|
|
34
|
+
Whether to show the widget immediately. Default False.
|
|
35
|
+
autoswap : bool
|
|
36
|
+
Whether to swap the buffers automatically after a draw event.
|
|
37
|
+
Default True. If True, the ``swap_buffers`` Canvas method will
|
|
38
|
+
be called last (by default) by the ``canvas.draw`` event handler.
|
|
39
|
+
app : Application | str
|
|
40
|
+
Give vispy Application instance to use as a backend.
|
|
41
|
+
(vispy.app is used by default.) If str, then an application
|
|
42
|
+
using the chosen backend (e.g., 'pyglet') will be created.
|
|
43
|
+
Note the canvas application can be accessed at ``canvas.app``.
|
|
44
|
+
create_native : bool
|
|
45
|
+
Whether to create the widget immediately. Default True.
|
|
46
|
+
vsync : bool
|
|
47
|
+
Enable vertical synchronization.
|
|
48
|
+
resizable : bool
|
|
49
|
+
Allow the window to be resized.
|
|
50
|
+
decorate : bool
|
|
51
|
+
Decorate the window. Default True.
|
|
52
|
+
fullscreen : bool | int
|
|
53
|
+
If False, windowed mode is used (default). If True, the default
|
|
54
|
+
monitor is used. If int, the given monitor number is used.
|
|
55
|
+
config : dict
|
|
56
|
+
A dict with OpenGL configuration options, which is combined
|
|
57
|
+
with the default configuration options and used to initialize
|
|
58
|
+
the context. See ``canvas.context.config`` for possible
|
|
59
|
+
options.
|
|
60
|
+
shared : Canvas | GLContext | None
|
|
61
|
+
An existing canvas or context to share OpenGL objects with.
|
|
62
|
+
keys : str | dict | None
|
|
63
|
+
Default key mapping to use. If 'interactive', escape and F11 will
|
|
64
|
+
close the canvas and toggle full-screen mode, respectively.
|
|
65
|
+
If dict, maps keys to functions. If dict values are strings,
|
|
66
|
+
they are assumed to be ``Canvas`` methods, otherwise they should
|
|
67
|
+
be callable.
|
|
68
|
+
parent : widget-object
|
|
69
|
+
The parent widget if this makes sense for the used backend.
|
|
70
|
+
dpi : float | None
|
|
71
|
+
Resolution in dots-per-inch to use for the canvas. If dpi is None,
|
|
72
|
+
then the value will be determined by querying the global config first,
|
|
73
|
+
and then the operating system.
|
|
74
|
+
always_on_top : bool
|
|
75
|
+
If True, try to create the window in always-on-top mode.
|
|
76
|
+
px_scale : int > 0
|
|
77
|
+
A scale factor to apply between logical and physical pixels in addition
|
|
78
|
+
to the actual scale factor determined by the backend. This option
|
|
79
|
+
allows the scale factor to be adjusted for testing.
|
|
80
|
+
bgcolor : Color
|
|
81
|
+
The background color to use.
|
|
82
|
+
|
|
83
|
+
See also
|
|
84
|
+
--------
|
|
85
|
+
vispy.app.Canvas
|
|
86
|
+
|
|
87
|
+
Notes
|
|
88
|
+
-----
|
|
89
|
+
Receives the following events:
|
|
90
|
+
|
|
91
|
+
* initialize
|
|
92
|
+
* resize
|
|
93
|
+
* draw
|
|
94
|
+
* mouse_press
|
|
95
|
+
* mouse_release
|
|
96
|
+
* mouse_double_click
|
|
97
|
+
* mouse_move
|
|
98
|
+
* mouse_wheel
|
|
99
|
+
* key_press
|
|
100
|
+
* key_release
|
|
101
|
+
* stylus
|
|
102
|
+
* touch
|
|
103
|
+
* close
|
|
104
|
+
|
|
105
|
+
The ordering of the mouse_double_click, mouse_press, and mouse_release
|
|
106
|
+
events are not guaranteed to be consistent between backends. Only certain
|
|
107
|
+
backends natively support double-clicking (currently Qt and WX); on other
|
|
108
|
+
backends, they are detected manually with a fixed time delay.
|
|
109
|
+
This can cause problems with accessibility, as increasing the OS detection
|
|
110
|
+
time or using a dedicated double-click button will not be respected.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, title='VisPy canvas', size=(800, 600), position=None,
|
|
114
|
+
show=False, autoswap=True, app=None, create_native=True,
|
|
115
|
+
vsync=False, resizable=True, decorate=True, fullscreen=False,
|
|
116
|
+
config=None, shared=None, keys=None, parent=None, dpi=None,
|
|
117
|
+
always_on_top=False, px_scale=1, bgcolor='black'):
|
|
118
|
+
self._scene = None
|
|
119
|
+
# A default widget that follows the shape of the canvas
|
|
120
|
+
self._central_widget = None
|
|
121
|
+
self._draw_order = weakref.WeakKeyDictionary()
|
|
122
|
+
self._drawing = False
|
|
123
|
+
self._update_pending = False
|
|
124
|
+
self._fb_stack = []
|
|
125
|
+
self._vp_stack = []
|
|
126
|
+
self._mouse_handler = None
|
|
127
|
+
self.transforms = TransformSystem(canvas=self)
|
|
128
|
+
self._bgcolor = Color(bgcolor).rgba
|
|
129
|
+
|
|
130
|
+
# Set to True to enable sending mouse events even when no button is
|
|
131
|
+
# pressed. Disabled by default because it is very expensive. Also
|
|
132
|
+
# private for now because this behavior / API needs more thought.
|
|
133
|
+
self._send_hover_events = False
|
|
134
|
+
|
|
135
|
+
super(SceneCanvas, self).__init__(
|
|
136
|
+
title, size, position, show, autoswap, app, create_native, vsync,
|
|
137
|
+
resizable, decorate, fullscreen, config, shared, keys, parent, dpi,
|
|
138
|
+
always_on_top, px_scale)
|
|
139
|
+
self.events.mouse_press.connect(self._process_mouse_event)
|
|
140
|
+
self.events.mouse_move.connect(self._process_mouse_event)
|
|
141
|
+
self.events.mouse_release.connect(self._process_mouse_event)
|
|
142
|
+
self.events.mouse_wheel.connect(self._process_mouse_event)
|
|
143
|
+
self.events.touch.connect(self._process_mouse_event)
|
|
144
|
+
|
|
145
|
+
self.scene = SubScene()
|
|
146
|
+
self.freeze()
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def scene(self):
|
|
150
|
+
"""The SubScene object that represents the root node of the
|
|
151
|
+
scene graph to be displayed.
|
|
152
|
+
"""
|
|
153
|
+
return self._scene
|
|
154
|
+
|
|
155
|
+
@scene.setter
|
|
156
|
+
def scene(self, node):
|
|
157
|
+
oldscene = self._scene
|
|
158
|
+
self._scene = node
|
|
159
|
+
if oldscene is not None:
|
|
160
|
+
oldscene._set_canvas(None)
|
|
161
|
+
oldscene.events.children_change.disconnect(self._update_scenegraph)
|
|
162
|
+
if node is not None:
|
|
163
|
+
node._set_canvas(self)
|
|
164
|
+
node.events.children_change.connect(self._update_scenegraph)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def central_widget(self):
|
|
168
|
+
"""Returns the default widget that occupies the entire area of the
|
|
169
|
+
canvas.
|
|
170
|
+
"""
|
|
171
|
+
if self._central_widget is None:
|
|
172
|
+
self._central_widget = Widget(size=self.size, parent=self.scene)
|
|
173
|
+
return self._central_widget
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def bgcolor(self):
|
|
177
|
+
return Color(self._bgcolor)
|
|
178
|
+
|
|
179
|
+
@bgcolor.setter
|
|
180
|
+
def bgcolor(self, color):
|
|
181
|
+
self._bgcolor = Color(color).rgba
|
|
182
|
+
if hasattr(self, '_backend'):
|
|
183
|
+
self.update()
|
|
184
|
+
|
|
185
|
+
def update(self, node=None):
|
|
186
|
+
"""Update the scene
|
|
187
|
+
|
|
188
|
+
Parameters
|
|
189
|
+
----------
|
|
190
|
+
node : instance of Node
|
|
191
|
+
Not used.
|
|
192
|
+
"""
|
|
193
|
+
# TODO: use node bounds to keep track of minimum drawable area
|
|
194
|
+
if self._drawing:
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
# Keep things civil in the node update system. Once an update
|
|
198
|
+
# has been scheduled, there is no need to flood the event queue
|
|
199
|
+
# of the backend with additional updates.
|
|
200
|
+
if not self._update_pending:
|
|
201
|
+
self._update_pending = True
|
|
202
|
+
super(SceneCanvas, self).update()
|
|
203
|
+
|
|
204
|
+
def on_draw(self, event):
|
|
205
|
+
"""Draw handler
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
event : instance of Event
|
|
210
|
+
The draw event.
|
|
211
|
+
"""
|
|
212
|
+
if self._scene is None:
|
|
213
|
+
return # Can happen on initialization
|
|
214
|
+
logger.debug('Canvas draw')
|
|
215
|
+
|
|
216
|
+
# Now that a draw event is going to be handled, open up the
|
|
217
|
+
# scheduling of further updates
|
|
218
|
+
self._update_pending = False
|
|
219
|
+
self._draw_scene()
|
|
220
|
+
|
|
221
|
+
def render(self, region=None, size=None, bgcolor=None, crop=None, alpha=True):
|
|
222
|
+
"""Render the scene to an offscreen buffer and return the image array.
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
region : tuple | None
|
|
227
|
+
Specifies the region of the canvas to render. Format is
|
|
228
|
+
(x, y, w, h). By default, the entire canvas is rendered.
|
|
229
|
+
size : tuple | None
|
|
230
|
+
Specifies the size of the image array to return. If no size is
|
|
231
|
+
given, then the size of the *region* is used, multiplied by the
|
|
232
|
+
pixel scaling factor of the canvas (see `pixel_scale`). This
|
|
233
|
+
argument allows the scene to be rendered at resolutions different
|
|
234
|
+
from the native canvas resolution.
|
|
235
|
+
bgcolor : instance of Color | None
|
|
236
|
+
The background color to use.
|
|
237
|
+
crop : array-like | None
|
|
238
|
+
If specified it determines the pixels read from the framebuffer.
|
|
239
|
+
In the format (x, y, w, h), relative to the region being rendered.
|
|
240
|
+
alpha : bool
|
|
241
|
+
If True (default) produce an RGBA array (h, w, 4). If False,
|
|
242
|
+
remove the Alpha channel and return the RGB array (h, w, 3).
|
|
243
|
+
This may be useful if blending of various elements requires a
|
|
244
|
+
solid background to produce the expected visualization.
|
|
245
|
+
|
|
246
|
+
Returns
|
|
247
|
+
-------
|
|
248
|
+
image : array
|
|
249
|
+
Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
|
|
250
|
+
upper-left corner of the rendered region. If ``alpha`` is ``False``,
|
|
251
|
+
then only 3 channels will be returned (RGB).
|
|
252
|
+
|
|
253
|
+
"""
|
|
254
|
+
self.set_current()
|
|
255
|
+
# Set up a framebuffer to render to
|
|
256
|
+
offset = (0, 0) if region is None else region[:2]
|
|
257
|
+
csize = self.size if region is None else region[2:]
|
|
258
|
+
s = self.pixel_scale
|
|
259
|
+
size = tuple([int(x * s) for x in csize]) if size is None else size
|
|
260
|
+
fbo = gloo.FrameBuffer(color=gloo.RenderBuffer(size[::-1]),
|
|
261
|
+
depth=gloo.RenderBuffer(size[::-1]))
|
|
262
|
+
|
|
263
|
+
self.push_fbo(fbo, offset, csize)
|
|
264
|
+
try:
|
|
265
|
+
self._draw_scene(bgcolor=bgcolor)
|
|
266
|
+
result = fbo.read(crop=crop)
|
|
267
|
+
finally:
|
|
268
|
+
self.pop_fbo()
|
|
269
|
+
|
|
270
|
+
if not alpha:
|
|
271
|
+
result = result[..., :3]
|
|
272
|
+
return result
|
|
273
|
+
|
|
274
|
+
def _draw_scene(self, bgcolor=None):
|
|
275
|
+
if bgcolor is None:
|
|
276
|
+
bgcolor = self._bgcolor
|
|
277
|
+
self.context.clear(color=bgcolor, depth=True)
|
|
278
|
+
self.draw_visual(self.scene)
|
|
279
|
+
|
|
280
|
+
def draw_visual(self, visual, event=None):
|
|
281
|
+
"""Draw a visual and its children to the canvas or currently active
|
|
282
|
+
framebuffer.
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
visual : Visual
|
|
287
|
+
The visual to draw
|
|
288
|
+
event : None or DrawEvent
|
|
289
|
+
Optionally specifies the original canvas draw event that initiated
|
|
290
|
+
this draw.
|
|
291
|
+
"""
|
|
292
|
+
prof = Profiler()
|
|
293
|
+
|
|
294
|
+
# make sure this canvas's context is active
|
|
295
|
+
self.set_current()
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
self._drawing = True
|
|
299
|
+
# get order to draw visuals
|
|
300
|
+
if visual not in self._draw_order:
|
|
301
|
+
self._draw_order[visual] = self._generate_draw_order()
|
|
302
|
+
order = self._draw_order[visual]
|
|
303
|
+
|
|
304
|
+
# draw (while avoiding branches with visible=False)
|
|
305
|
+
stack = []
|
|
306
|
+
invisible_node = None
|
|
307
|
+
for node, start in order:
|
|
308
|
+
if start:
|
|
309
|
+
stack.append(node)
|
|
310
|
+
if invisible_node is None:
|
|
311
|
+
if not node.visible:
|
|
312
|
+
# disable drawing until we exit this node's subtree
|
|
313
|
+
invisible_node = node
|
|
314
|
+
else:
|
|
315
|
+
if hasattr(node, 'draw'):
|
|
316
|
+
node.draw()
|
|
317
|
+
prof.mark(str(node))
|
|
318
|
+
else:
|
|
319
|
+
if node is invisible_node:
|
|
320
|
+
invisible_node = None
|
|
321
|
+
stack.pop()
|
|
322
|
+
finally:
|
|
323
|
+
self._drawing = False
|
|
324
|
+
|
|
325
|
+
def _generate_draw_order(self, node=None):
|
|
326
|
+
"""Return a list giving the order to draw visuals.
|
|
327
|
+
|
|
328
|
+
Each node appears twice in the list--(node, True) appears before the
|
|
329
|
+
node's children are drawn, and (node, False) appears after.
|
|
330
|
+
"""
|
|
331
|
+
if node is None:
|
|
332
|
+
node = self._scene
|
|
333
|
+
order = [(node, True)]
|
|
334
|
+
children = node.children
|
|
335
|
+
children.sort(key=lambda ch: ch.order)
|
|
336
|
+
for ch in children:
|
|
337
|
+
order.extend(self._generate_draw_order(ch))
|
|
338
|
+
order.append((node, False))
|
|
339
|
+
return order
|
|
340
|
+
|
|
341
|
+
def _update_scenegraph(self, event):
|
|
342
|
+
"""Called when topology of scenegraph has changed."""
|
|
343
|
+
self._draw_order.clear()
|
|
344
|
+
self.update()
|
|
345
|
+
|
|
346
|
+
def _process_mouse_event(self, event):
|
|
347
|
+
prof = Profiler() # noqa
|
|
348
|
+
deliver_types = [
|
|
349
|
+
'mouse_press',
|
|
350
|
+
'mouse_wheel',
|
|
351
|
+
'gesture_zoom',
|
|
352
|
+
'gesture_rotate',
|
|
353
|
+
]
|
|
354
|
+
if self._send_hover_events:
|
|
355
|
+
deliver_types += ['mouse_move']
|
|
356
|
+
|
|
357
|
+
picked = self._mouse_handler
|
|
358
|
+
if picked is None:
|
|
359
|
+
if event.type in deliver_types:
|
|
360
|
+
picked = self.visual_at(event.pos)
|
|
361
|
+
|
|
362
|
+
# No visual to handle this event; bail out now
|
|
363
|
+
if picked is None:
|
|
364
|
+
return
|
|
365
|
+
|
|
366
|
+
# Create an event to pass to the picked visual
|
|
367
|
+
scene_event = SceneMouseEvent(event=event, visual=picked)
|
|
368
|
+
|
|
369
|
+
# Deliver the event
|
|
370
|
+
if picked == self._mouse_handler:
|
|
371
|
+
# If we already have a mouse handler, then no other node may
|
|
372
|
+
# receive the event
|
|
373
|
+
if event.type == 'mouse_release':
|
|
374
|
+
self._mouse_handler = None
|
|
375
|
+
getattr(picked.events, event.type)(scene_event)
|
|
376
|
+
else:
|
|
377
|
+
# If we don't have a mouse handler, then pass the event through
|
|
378
|
+
# the chain of parents until a node accepts the event.
|
|
379
|
+
while picked is not None:
|
|
380
|
+
getattr(picked.events, event.type)(scene_event)
|
|
381
|
+
if scene_event.handled:
|
|
382
|
+
if event.type == 'mouse_press':
|
|
383
|
+
self._mouse_handler = picked
|
|
384
|
+
break
|
|
385
|
+
if event.type in deliver_types:
|
|
386
|
+
# events that are not handled get passed to parent
|
|
387
|
+
picked = picked.parent
|
|
388
|
+
scene_event.visual = picked
|
|
389
|
+
else:
|
|
390
|
+
picked = None
|
|
391
|
+
|
|
392
|
+
# If something in the scene handled the scene_event, then we mark
|
|
393
|
+
# the original event accordingly.
|
|
394
|
+
event.handled = scene_event.handled
|
|
395
|
+
|
|
396
|
+
def visual_at(self, pos):
|
|
397
|
+
"""Return the visual at a given position
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
pos : tuple
|
|
402
|
+
The position in logical coordinates to query.
|
|
403
|
+
|
|
404
|
+
Returns
|
|
405
|
+
-------
|
|
406
|
+
visual : instance of Visual | None
|
|
407
|
+
The visual at the position, if it exists.
|
|
408
|
+
"""
|
|
409
|
+
tr = self.transforms.get_transform('canvas', 'framebuffer')
|
|
410
|
+
fbpos = tr.map(pos)[:2]
|
|
411
|
+
|
|
412
|
+
try:
|
|
413
|
+
id_ = self._render_picking((fbpos[0], fbpos[1], 1, 1))
|
|
414
|
+
vis = VisualNode._visual_ids.get(id_[0, 0], None)
|
|
415
|
+
except RuntimeError:
|
|
416
|
+
# Don't have read_pixels() support for IPython. Fall back to
|
|
417
|
+
# bounds checking.
|
|
418
|
+
return self._visual_bounds_at(pos)
|
|
419
|
+
return vis
|
|
420
|
+
|
|
421
|
+
def _visual_bounds_at(self, pos, node=None):
|
|
422
|
+
"""Find a visual whose bounding rect encompasses *pos*."""
|
|
423
|
+
if node is None:
|
|
424
|
+
node = self.scene
|
|
425
|
+
|
|
426
|
+
for ch in node.children:
|
|
427
|
+
hit = self._visual_bounds_at(pos, ch)
|
|
428
|
+
if hit is not None:
|
|
429
|
+
return hit
|
|
430
|
+
|
|
431
|
+
if (not isinstance(node, VisualNode) or not node.visible or
|
|
432
|
+
not node.interactive):
|
|
433
|
+
return None
|
|
434
|
+
|
|
435
|
+
# let nodes know we are picking to handle any special cases (picking meshes)
|
|
436
|
+
# we can't do this before this or child nodes may be considered visible
|
|
437
|
+
# which would cause the above 'if' statement to pass when it shouldn't
|
|
438
|
+
node.picking = True
|
|
439
|
+
bounds = [node.bounds(axis=i) for i in range(2)]
|
|
440
|
+
node.picking = False
|
|
441
|
+
|
|
442
|
+
if None in bounds:
|
|
443
|
+
return None
|
|
444
|
+
|
|
445
|
+
tr = self.scene.node_transform(node).inverse
|
|
446
|
+
corners = np.array([
|
|
447
|
+
[bounds[0][0], bounds[1][0]],
|
|
448
|
+
[bounds[0][0], bounds[1][1]],
|
|
449
|
+
[bounds[0][1], bounds[1][0]],
|
|
450
|
+
[bounds[0][1], bounds[1][1]]])
|
|
451
|
+
bounds = tr.map(corners)
|
|
452
|
+
xhit = bounds[:, 0].min() < pos[0] < bounds[:, 0].max()
|
|
453
|
+
yhit = bounds[:, 1].min() < pos[1] < bounds[:, 1].max()
|
|
454
|
+
if xhit and yhit:
|
|
455
|
+
return node
|
|
456
|
+
|
|
457
|
+
def visuals_at(self, pos, radius=10):
|
|
458
|
+
"""Return a list of visuals within *radius* pixels of *pos*.
|
|
459
|
+
|
|
460
|
+
Visuals are sorted by their proximity to *pos*.
|
|
461
|
+
|
|
462
|
+
Parameters
|
|
463
|
+
----------
|
|
464
|
+
pos : tuple
|
|
465
|
+
(x, y) position at which to find visuals.
|
|
466
|
+
radius : int
|
|
467
|
+
Distance away from *pos* to search for visuals.
|
|
468
|
+
"""
|
|
469
|
+
tr = self.transforms.get_transform('canvas', 'framebuffer')
|
|
470
|
+
pos = tr.map(pos)[:2]
|
|
471
|
+
|
|
472
|
+
id = self._render_picking((pos[0]-radius, pos[1]-radius,
|
|
473
|
+
radius * 2 + 1, radius * 2 + 1))
|
|
474
|
+
ids = []
|
|
475
|
+
seen = set()
|
|
476
|
+
for i in range(radius):
|
|
477
|
+
subr = id[radius-i:radius+i+1, radius-i:radius+i+1]
|
|
478
|
+
subr_ids = set(list(np.unique(subr)))
|
|
479
|
+
ids.extend(list(subr_ids - seen))
|
|
480
|
+
seen |= subr_ids
|
|
481
|
+
visuals = [VisualNode._visual_ids.get(x, None) for x in ids]
|
|
482
|
+
return [v for v in visuals if v is not None]
|
|
483
|
+
|
|
484
|
+
def _render_picking(self, crop):
|
|
485
|
+
"""Render the scene in picking mode, returning a 2D array of visual
|
|
486
|
+
IDs in the area specified by crop.
|
|
487
|
+
|
|
488
|
+
Parameters
|
|
489
|
+
----------
|
|
490
|
+
crop : array-like
|
|
491
|
+
The crop (x, y, w, h) of the framebuffer to read. For picking the
|
|
492
|
+
full canvas is rendered and cropped on read as it is much faster
|
|
493
|
+
than triggering transform updates across the scene with every
|
|
494
|
+
click.
|
|
495
|
+
"""
|
|
496
|
+
with self._scene.set_picking():
|
|
497
|
+
img = self.render(bgcolor=(0, 0, 0, 0), crop=crop)
|
|
498
|
+
img = img.astype('int32') * [2**0, 2**8, 2**16, 2**24]
|
|
499
|
+
id_ = img.sum(axis=2).astype('int32')
|
|
500
|
+
return id_
|
|
501
|
+
|
|
502
|
+
def on_resize(self, event):
|
|
503
|
+
"""Resize handler
|
|
504
|
+
|
|
505
|
+
Parameters
|
|
506
|
+
----------
|
|
507
|
+
event : instance of Event
|
|
508
|
+
The resize event.
|
|
509
|
+
"""
|
|
510
|
+
self._update_transforms()
|
|
511
|
+
|
|
512
|
+
if self._central_widget is not None:
|
|
513
|
+
self._central_widget.size = self.size
|
|
514
|
+
|
|
515
|
+
if len(self._vp_stack) == 0:
|
|
516
|
+
self.context.set_viewport(0, 0, *self.physical_size)
|
|
517
|
+
|
|
518
|
+
def on_close(self, event):
|
|
519
|
+
"""Close event handler
|
|
520
|
+
|
|
521
|
+
Parameters
|
|
522
|
+
----------
|
|
523
|
+
event : instance of Event
|
|
524
|
+
The event.
|
|
525
|
+
"""
|
|
526
|
+
self.events.mouse_press.disconnect(self._process_mouse_event)
|
|
527
|
+
self.events.mouse_move.disconnect(self._process_mouse_event)
|
|
528
|
+
self.events.mouse_release.disconnect(self._process_mouse_event)
|
|
529
|
+
self.events.mouse_wheel.disconnect(self._process_mouse_event)
|
|
530
|
+
self.events.touch.disconnect(self._process_mouse_event)
|
|
531
|
+
|
|
532
|
+
# -------------------------------------------------- transform handling ---
|
|
533
|
+
def push_viewport(self, viewport):
|
|
534
|
+
"""Push a viewport (x, y, w, h) on the stack. Values must be integers
|
|
535
|
+
relative to the active framebuffer.
|
|
536
|
+
|
|
537
|
+
Parameters
|
|
538
|
+
----------
|
|
539
|
+
viewport : tuple
|
|
540
|
+
The viewport as (x, y, w, h).
|
|
541
|
+
"""
|
|
542
|
+
vp = list(viewport)
|
|
543
|
+
# Normalize viewport before setting;
|
|
544
|
+
if vp[2] < 0:
|
|
545
|
+
vp[0] += vp[2]
|
|
546
|
+
vp[2] *= -1
|
|
547
|
+
if vp[3] < 0:
|
|
548
|
+
vp[1] += vp[3]
|
|
549
|
+
vp[3] *= -1
|
|
550
|
+
|
|
551
|
+
self._vp_stack.append(vp)
|
|
552
|
+
try:
|
|
553
|
+
self.context.set_viewport(*vp)
|
|
554
|
+
except Exception:
|
|
555
|
+
self._vp_stack.pop()
|
|
556
|
+
raise
|
|
557
|
+
|
|
558
|
+
self._update_transforms()
|
|
559
|
+
|
|
560
|
+
def pop_viewport(self):
|
|
561
|
+
"""Pop a viewport from the stack."""
|
|
562
|
+
vp = self._vp_stack.pop()
|
|
563
|
+
# Activate latest
|
|
564
|
+
if len(self._vp_stack) > 0:
|
|
565
|
+
self.context.set_viewport(*self._vp_stack[-1])
|
|
566
|
+
else:
|
|
567
|
+
self.context.set_viewport(0, 0, *self.physical_size)
|
|
568
|
+
|
|
569
|
+
self._update_transforms()
|
|
570
|
+
return vp
|
|
571
|
+
|
|
572
|
+
def push_fbo(self, fbo, offset, csize):
|
|
573
|
+
"""Push an FBO on the stack.
|
|
574
|
+
|
|
575
|
+
This activates the framebuffer and causes subsequent rendering to be
|
|
576
|
+
written to the framebuffer rather than the canvas's back buffer. This
|
|
577
|
+
will also set the canvas viewport to cover the boundaries of the
|
|
578
|
+
framebuffer.
|
|
579
|
+
|
|
580
|
+
Parameters
|
|
581
|
+
----------
|
|
582
|
+
fbo : instance of FrameBuffer
|
|
583
|
+
The framebuffer object .
|
|
584
|
+
offset : tuple
|
|
585
|
+
The location of the fbo origin relative to the canvas's framebuffer
|
|
586
|
+
origin.
|
|
587
|
+
csize : tuple
|
|
588
|
+
The size of the region in the canvas's framebuffer that should be
|
|
589
|
+
covered by this framebuffer object.
|
|
590
|
+
"""
|
|
591
|
+
self._fb_stack.append((fbo, offset, csize))
|
|
592
|
+
try:
|
|
593
|
+
fbo.activate()
|
|
594
|
+
h, w = fbo.color_buffer.shape[:2]
|
|
595
|
+
self.push_viewport((0, 0, w, h))
|
|
596
|
+
except Exception:
|
|
597
|
+
self._fb_stack.pop()
|
|
598
|
+
raise
|
|
599
|
+
|
|
600
|
+
self._update_transforms()
|
|
601
|
+
|
|
602
|
+
def pop_fbo(self):
|
|
603
|
+
"""Pop an FBO from the stack."""
|
|
604
|
+
fbo = self._fb_stack.pop()
|
|
605
|
+
fbo[0].deactivate()
|
|
606
|
+
self.pop_viewport()
|
|
607
|
+
if len(self._fb_stack) > 0:
|
|
608
|
+
old_fbo = self._fb_stack[-1]
|
|
609
|
+
old_fbo[0].activate()
|
|
610
|
+
|
|
611
|
+
self._update_transforms()
|
|
612
|
+
return fbo
|
|
613
|
+
|
|
614
|
+
def _current_framebuffer(self):
|
|
615
|
+
"""Return (fbo, origin, canvas_size) for the current
|
|
616
|
+
FBO on the stack, or for the canvas if there is no FBO.
|
|
617
|
+
"""
|
|
618
|
+
if len(self._fb_stack) == 0:
|
|
619
|
+
return None, (0, 0), self.size
|
|
620
|
+
else:
|
|
621
|
+
return self._fb_stack[-1]
|
|
622
|
+
|
|
623
|
+
def _update_transforms(self):
|
|
624
|
+
"""Update the canvas's TransformSystem to correct for the current
|
|
625
|
+
canvas size, framebuffer, and viewport.
|
|
626
|
+
"""
|
|
627
|
+
if len(self._fb_stack) == 0:
|
|
628
|
+
fb_size = fb_rect = None
|
|
629
|
+
else:
|
|
630
|
+
fb, origin, fb_size = self._fb_stack[-1]
|
|
631
|
+
fb_rect = origin + fb_size
|
|
632
|
+
|
|
633
|
+
if len(self._vp_stack) == 0:
|
|
634
|
+
viewport = None
|
|
635
|
+
else:
|
|
636
|
+
viewport = self._vp_stack[-1]
|
|
637
|
+
|
|
638
|
+
self.transforms.configure(viewport=viewport, fbo_size=fb_size,
|
|
639
|
+
fbo_rect=fb_rect)
|