vispy 0.15.0__cp313-cp313-win_amd64.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.cp313-win_amd64.pyd +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 +5 -0
- vispy-0.15.0.dist-info/licenses/LICENSE.txt +36 -0
- vispy-0.15.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,474 @@
|
|
|
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 math
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from ...app import Timer
|
|
11
|
+
from ...util.quaternion import Quaternion
|
|
12
|
+
from ...util import keys
|
|
13
|
+
from .perspective import PerspectiveCamera
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FlyCamera(PerspectiveCamera):
|
|
17
|
+
"""The fly camera provides a way to explore 3D data using an
|
|
18
|
+
interaction style that resembles a flight simulator.
|
|
19
|
+
|
|
20
|
+
For this camera, the ``scale_factor`` indicates the speed of the
|
|
21
|
+
camera in units per second, and the ``center`` indicates the
|
|
22
|
+
position of the camera.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
fov : float
|
|
27
|
+
Field of view. Default 60.0.
|
|
28
|
+
rotation : float | None
|
|
29
|
+
Rotation to use.
|
|
30
|
+
**kwargs : dict
|
|
31
|
+
Keyword arguments to pass to `BaseCamera`.
|
|
32
|
+
|
|
33
|
+
Notes
|
|
34
|
+
-----
|
|
35
|
+
Interacting with this camera might need a bit of practice.
|
|
36
|
+
The reaction to key presses can be customized by modifying the
|
|
37
|
+
keymap property.
|
|
38
|
+
|
|
39
|
+
Moving:
|
|
40
|
+
|
|
41
|
+
* arrow keys, or WASD to move forward, backward, left and right
|
|
42
|
+
* F and C keys move up and down
|
|
43
|
+
* Space bar to brake
|
|
44
|
+
|
|
45
|
+
Viewing:
|
|
46
|
+
|
|
47
|
+
* Use the mouse while holding down LMB to control the pitch and yaw.
|
|
48
|
+
* Alternatively, the pitch and yaw can be changed using the keys
|
|
49
|
+
IKJL
|
|
50
|
+
* The camera auto-rotates to make the bottom point down, manual
|
|
51
|
+
rolling can be performed using Q and E.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# Using _rotation1 and _rotation2 for camera states instead of _rotation
|
|
56
|
+
_state_props = PerspectiveCamera._state_props + ('rotation1', 'rotation2')
|
|
57
|
+
|
|
58
|
+
def __init__(self, fov=60, rotation=None, **kwargs):
|
|
59
|
+
|
|
60
|
+
# Motion speed vector
|
|
61
|
+
self._speed = np.zeros((6,), 'float64')
|
|
62
|
+
self._distance = None
|
|
63
|
+
|
|
64
|
+
# Acceleration and braking vectors, set from keyboard
|
|
65
|
+
self._brake = np.zeros((6,), 'uint8') # bool-ish
|
|
66
|
+
self._acc = np.zeros((6,), 'float64')
|
|
67
|
+
|
|
68
|
+
# Init rotations
|
|
69
|
+
self._auto_roll = True # Whether to roll to make Z up
|
|
70
|
+
self._rotation1 = Quaternion() # The base rotation
|
|
71
|
+
self._rotation2 = Quaternion() # The delta yaw and pitch rotation
|
|
72
|
+
|
|
73
|
+
PerspectiveCamera.__init__(self, fov=fov, **kwargs)
|
|
74
|
+
|
|
75
|
+
# Set camera attributes
|
|
76
|
+
self.rotation1 = rotation.normalize() if (rotation is not None) else Quaternion()
|
|
77
|
+
|
|
78
|
+
# To store data at start of interaction
|
|
79
|
+
self._event_value = None
|
|
80
|
+
|
|
81
|
+
# Whether the mouse-system wants a transform update
|
|
82
|
+
self._update_from_mouse = False
|
|
83
|
+
|
|
84
|
+
# Mapping that defines keys to thrusters
|
|
85
|
+
self._keymap = {
|
|
86
|
+
keys.UP: (+1, 1), keys.DOWN: (-1, 1),
|
|
87
|
+
keys.RIGHT: (+1, 2), keys.LEFT: (-1, 2),
|
|
88
|
+
#
|
|
89
|
+
'W': (+1, 1), 'S': (-1, 1),
|
|
90
|
+
'D': (+1, 2), 'A': (-1, 2),
|
|
91
|
+
'F': (+1, 3), 'C': (-1, 3),
|
|
92
|
+
#
|
|
93
|
+
'I': (+1, 4), 'K': (-1, 4),
|
|
94
|
+
'L': (+1, 5), 'J': (-1, 5),
|
|
95
|
+
'Q': (+1, 6), 'E': (-1, 6),
|
|
96
|
+
#
|
|
97
|
+
keys.SPACE: (0, 1, 2, 3), # 0 means brake, apply to translation
|
|
98
|
+
# keys.ALT: (+5, 1), # Turbo
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# Timer. Each tick we calculate new speed and new position
|
|
102
|
+
self._timer = Timer(0.01, start=False, connect=self.on_timer)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def rotation(self):
|
|
106
|
+
"""Get the full rotation. This rotation is composed of the
|
|
107
|
+
normal rotation plus the extra rotation due to the current
|
|
108
|
+
interaction of the user.
|
|
109
|
+
"""
|
|
110
|
+
rotation = self._rotation2 * self._rotation1
|
|
111
|
+
return rotation.normalize()
|
|
112
|
+
|
|
113
|
+
@rotation.setter
|
|
114
|
+
def rotation(self, value):
|
|
115
|
+
print("rotation.setter called, use rotation1.setter instead")
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def rotation1(self):
|
|
119
|
+
"""rotation1 records confirmed camera rotation"""
|
|
120
|
+
return self._rotation1
|
|
121
|
+
|
|
122
|
+
@rotation1.setter
|
|
123
|
+
def rotation1(self, value):
|
|
124
|
+
assert isinstance(value, Quaternion)
|
|
125
|
+
self._rotation1 = value.normalize()
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def rotation2(self):
|
|
129
|
+
"""rotation2 records on going camera rotation."""
|
|
130
|
+
return self._rotation2
|
|
131
|
+
|
|
132
|
+
@rotation2.setter
|
|
133
|
+
def rotation2(self, value):
|
|
134
|
+
assert isinstance(value, Quaternion)
|
|
135
|
+
self._rotation2 = value.normalize()
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def auto_roll(self):
|
|
139
|
+
"""Whether to rotate the camera automaticall to try and attempt
|
|
140
|
+
to keep Z up.
|
|
141
|
+
"""
|
|
142
|
+
return self._auto_roll
|
|
143
|
+
|
|
144
|
+
@auto_roll.setter
|
|
145
|
+
def auto_roll(self, value):
|
|
146
|
+
self._auto_roll = bool(value)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def keymap(self):
|
|
150
|
+
"""A dictionary that maps keys to thruster directions
|
|
151
|
+
|
|
152
|
+
The keys in this dictionary are vispy key descriptions (from
|
|
153
|
+
vispy.keys) or characters that represent keys. These are matched
|
|
154
|
+
to the "key" attribute of key-press and key-release events.
|
|
155
|
+
|
|
156
|
+
The values are tuples, in which the first element specifies the
|
|
157
|
+
magnitude of the acceleration, using negative values for
|
|
158
|
+
"backward" thrust. A value of zero means to brake. The remaining
|
|
159
|
+
elements specify the dimension to which the acceleration should
|
|
160
|
+
be applied. These are 1, 2, 3 for forward/backward, left/right,
|
|
161
|
+
up/down, and 4, 5, 6 for pitch, yaw, roll.
|
|
162
|
+
"""
|
|
163
|
+
return self._keymap
|
|
164
|
+
|
|
165
|
+
def _set_range(self, init):
|
|
166
|
+
"""Reset the view."""
|
|
167
|
+
# PerspectiveCamera._set_range(self, init)
|
|
168
|
+
# Stop moving
|
|
169
|
+
self._speed *= 0.0
|
|
170
|
+
|
|
171
|
+
# Get window size (and store factor now to sync with resizing)
|
|
172
|
+
w, h = self._viewbox.size
|
|
173
|
+
w, h = float(w), float(h)
|
|
174
|
+
|
|
175
|
+
# Get range and translation for x and y
|
|
176
|
+
x1, y1, z1 = self._xlim[0], self._ylim[0], self._zlim[0]
|
|
177
|
+
x2, y2, z2 = self._xlim[1], self._ylim[1], self._zlim[1]
|
|
178
|
+
rx, ry, rz = (x2 - x1), (y2 - y1), (z2 - z1)
|
|
179
|
+
|
|
180
|
+
# Correct ranges for window size. Note that the window width
|
|
181
|
+
# influences the x and y data range, while the height influences
|
|
182
|
+
# the z data range.
|
|
183
|
+
if w / h > 1:
|
|
184
|
+
rx /= w / h
|
|
185
|
+
ry /= w / h
|
|
186
|
+
else:
|
|
187
|
+
rz /= h / w
|
|
188
|
+
|
|
189
|
+
# Do not convert to screen coordinates. This camera does not need
|
|
190
|
+
# to fit everything on screen, but we need to estimate the scale
|
|
191
|
+
# of the data in the scene.
|
|
192
|
+
|
|
193
|
+
# Set scale, depending on data range. Initial speed is such that
|
|
194
|
+
# the scene can be traversed in about three seconds.
|
|
195
|
+
self._scale_factor = max(rx, ry, rz) / 3.0
|
|
196
|
+
|
|
197
|
+
# Set initial position to a corner of the scene
|
|
198
|
+
margin = np.mean([rx, ry, rz]) * 0.1
|
|
199
|
+
self._center = x1 - margin, y1 - margin, z1 + margin
|
|
200
|
+
|
|
201
|
+
# Determine initial view direction based on flip axis
|
|
202
|
+
yaw = 45 * self._flip_factors[0]
|
|
203
|
+
pitch = -90 - 20 * self._flip_factors[2]
|
|
204
|
+
if self._flip_factors[1] < 0:
|
|
205
|
+
yaw += 90 * np.sign(self._flip_factors[0])
|
|
206
|
+
|
|
207
|
+
# Set orientation
|
|
208
|
+
q1 = Quaternion.create_from_axis_angle(pitch*math.pi/180, 1, 0, 0)
|
|
209
|
+
q2 = Quaternion.create_from_axis_angle(0*math.pi/180, 0, 1, 0)
|
|
210
|
+
q3 = Quaternion.create_from_axis_angle(yaw*math.pi/180, 0, 0, 1)
|
|
211
|
+
#
|
|
212
|
+
self._rotation1 = (q1 * q2 * q3).normalize()
|
|
213
|
+
self._rotation2 = Quaternion()
|
|
214
|
+
|
|
215
|
+
# Update
|
|
216
|
+
self.view_changed()
|
|
217
|
+
|
|
218
|
+
def _get_directions(self):
|
|
219
|
+
|
|
220
|
+
# Get reference points in reference coordinates
|
|
221
|
+
# p0 = Point(0,0,0)
|
|
222
|
+
pf = (0, 0, -1) # front
|
|
223
|
+
pr = (1, 0, 0) # right
|
|
224
|
+
pl = (-1, 0, 0) # left
|
|
225
|
+
pu = (0, 1, 0) # up
|
|
226
|
+
|
|
227
|
+
# Get total rotation
|
|
228
|
+
rotation = self.rotation.inverse()
|
|
229
|
+
|
|
230
|
+
# Transform to real coordinates
|
|
231
|
+
pf = rotation.rotate_point(pf)
|
|
232
|
+
pr = rotation.rotate_point(pr)
|
|
233
|
+
pl = rotation.rotate_point(pl)
|
|
234
|
+
pu = rotation.rotate_point(pu)
|
|
235
|
+
|
|
236
|
+
def _normalize(p):
|
|
237
|
+
L = sum(x**2 for x in p) ** 0.5
|
|
238
|
+
return np.array(p, 'float64') / L
|
|
239
|
+
|
|
240
|
+
pf = _normalize(pf)
|
|
241
|
+
pr = _normalize(pr)
|
|
242
|
+
pl = _normalize(pl)
|
|
243
|
+
pu = _normalize(pu)
|
|
244
|
+
|
|
245
|
+
return pf, pr, pl, pu
|
|
246
|
+
|
|
247
|
+
def on_timer(self, event):
|
|
248
|
+
"""Timer event handler
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
event : instance of Event
|
|
253
|
+
The event.
|
|
254
|
+
"""
|
|
255
|
+
# Set relative speed and acceleration
|
|
256
|
+
rel_speed = event.dt
|
|
257
|
+
rel_acc = 0.1
|
|
258
|
+
|
|
259
|
+
# Get what's forward
|
|
260
|
+
pf, pr, pl, pu = self._get_directions()
|
|
261
|
+
|
|
262
|
+
# Increase speed through acceleration
|
|
263
|
+
# Note that self._speed is relative. We can balance rel_acc and
|
|
264
|
+
# rel_speed to get a nice smooth or direct control
|
|
265
|
+
self._speed += self._acc * rel_acc
|
|
266
|
+
|
|
267
|
+
# Reduce speed. Simulate resistance. Using brakes slows down faster.
|
|
268
|
+
# Note that the way that we reduce speed, allows for higher
|
|
269
|
+
# speeds if keys ar bound to higher acc values (i.e. turbo)
|
|
270
|
+
reduce = np.array([0.05, 0.05, 0.05, 0.1, 0.1, 0.1])
|
|
271
|
+
reduce[self._brake > 0] = 0.2
|
|
272
|
+
self._speed -= self._speed * reduce
|
|
273
|
+
if np.abs(self._speed).max() < 0.05:
|
|
274
|
+
self._speed *= 0.0
|
|
275
|
+
|
|
276
|
+
# --- Determine new position from translation speed
|
|
277
|
+
|
|
278
|
+
if self._speed[:3].any():
|
|
279
|
+
|
|
280
|
+
# Create speed vectors, use scale_factor as a reference
|
|
281
|
+
dv = np.array([1.0/d for d in self._flip_factors])
|
|
282
|
+
#
|
|
283
|
+
vf = pf * dv * rel_speed * self._scale_factor
|
|
284
|
+
vr = pr * dv * rel_speed * self._scale_factor
|
|
285
|
+
vu = pu * dv * rel_speed * self._scale_factor
|
|
286
|
+
direction = vf, vr, vu
|
|
287
|
+
|
|
288
|
+
# Set position
|
|
289
|
+
center_loc = np.array(self._center, dtype='float32')
|
|
290
|
+
center_loc += (self._speed[0] * direction[0] +
|
|
291
|
+
self._speed[1] * direction[1] +
|
|
292
|
+
self._speed[2] * direction[2])
|
|
293
|
+
self._center = tuple(center_loc)
|
|
294
|
+
|
|
295
|
+
# --- Determine new orientation from rotation speed
|
|
296
|
+
|
|
297
|
+
roll_angle = 0
|
|
298
|
+
|
|
299
|
+
# Calculate manual roll (from speed)
|
|
300
|
+
if self._speed[3:].any():
|
|
301
|
+
angleGain = np.array([1.0, 1.5, 1.0]) * 3 * math.pi / 180
|
|
302
|
+
angles = self._speed[3:] * angleGain
|
|
303
|
+
|
|
304
|
+
q1 = Quaternion.create_from_axis_angle(angles[0], -1, 0, 0)
|
|
305
|
+
q2 = Quaternion.create_from_axis_angle(angles[1], 0, 1, 0)
|
|
306
|
+
q3 = Quaternion.create_from_axis_angle(angles[2], 0, 0, -1)
|
|
307
|
+
q = q1 * q2 * q3
|
|
308
|
+
self._rotation1 = (q * self._rotation1).normalize()
|
|
309
|
+
|
|
310
|
+
# Calculate auto-roll
|
|
311
|
+
if self.auto_roll:
|
|
312
|
+
up = {'x': (1, 0, 0), 'y': (0, 1, 0), 'z': (0, 0, 1)}[self.up[1]]
|
|
313
|
+
up = np.array(up) * {'+': +1, '-': -1}[self.up[0]]
|
|
314
|
+
|
|
315
|
+
def angle(p1, p2):
|
|
316
|
+
return np.arccos(p1.dot(p2))
|
|
317
|
+
# au = angle(pu, (0, 0, 1))
|
|
318
|
+
ar = angle(pr, up)
|
|
319
|
+
al = angle(pl, up)
|
|
320
|
+
af = angle(pf, up)
|
|
321
|
+
# Roll angle that's off from being leveled (in unit strength)
|
|
322
|
+
roll_angle = math.sin(0.5*(al - ar))
|
|
323
|
+
# Correct for pitch
|
|
324
|
+
roll_angle *= abs(math.sin(af)) # abs(math.sin(au))
|
|
325
|
+
if abs(roll_angle) < 0.05:
|
|
326
|
+
roll_angle = 0
|
|
327
|
+
if roll_angle:
|
|
328
|
+
# Correct to soften the force at 90 degree angle
|
|
329
|
+
roll_angle = np.sign(roll_angle) * np.abs(roll_angle)**0.5
|
|
330
|
+
# Get correction for this iteration and apply
|
|
331
|
+
angle_correction = 1.0 * roll_angle * math.pi / 180
|
|
332
|
+
q = Quaternion.create_from_axis_angle(angle_correction,
|
|
333
|
+
0, 0, 1)
|
|
334
|
+
self._rotation1 = (q * self._rotation1).normalize()
|
|
335
|
+
|
|
336
|
+
# Update
|
|
337
|
+
if self._speed.any() or roll_angle or self._update_from_mouse:
|
|
338
|
+
self._update_from_mouse = False
|
|
339
|
+
self.view_changed()
|
|
340
|
+
|
|
341
|
+
def viewbox_key_event(self, event):
|
|
342
|
+
"""The ViewBox key event handler
|
|
343
|
+
|
|
344
|
+
Parameters
|
|
345
|
+
----------
|
|
346
|
+
event : instance of Event
|
|
347
|
+
The event.
|
|
348
|
+
"""
|
|
349
|
+
PerspectiveCamera.viewbox_key_event(self, event)
|
|
350
|
+
|
|
351
|
+
if event.handled or not self.interactive:
|
|
352
|
+
return
|
|
353
|
+
|
|
354
|
+
# Ensure the timer runs
|
|
355
|
+
if not self._timer.running:
|
|
356
|
+
self._timer.start()
|
|
357
|
+
|
|
358
|
+
if event.key in self._keymap:
|
|
359
|
+
val_dims = self._keymap[event.key]
|
|
360
|
+
val = val_dims[0]
|
|
361
|
+
# Brake or accelarate?
|
|
362
|
+
if val == 0:
|
|
363
|
+
vec = self._brake
|
|
364
|
+
val = 1
|
|
365
|
+
else:
|
|
366
|
+
vec = self._acc
|
|
367
|
+
# Set
|
|
368
|
+
if event.type == 'key_release':
|
|
369
|
+
val = 0
|
|
370
|
+
for dim in val_dims[1:]:
|
|
371
|
+
factor = 1.0
|
|
372
|
+
vec[dim-1] = val * factor
|
|
373
|
+
event.handled = True
|
|
374
|
+
|
|
375
|
+
def viewbox_mouse_event(self, event):
|
|
376
|
+
"""The ViewBox mouse event handler
|
|
377
|
+
|
|
378
|
+
Parameters
|
|
379
|
+
----------
|
|
380
|
+
event : instance of Event
|
|
381
|
+
The event.
|
|
382
|
+
"""
|
|
383
|
+
PerspectiveCamera.viewbox_mouse_event(self, event)
|
|
384
|
+
|
|
385
|
+
if event.handled or not self.interactive:
|
|
386
|
+
return
|
|
387
|
+
|
|
388
|
+
if event.type == 'mouse_wheel':
|
|
389
|
+
if not event.mouse_event.modifiers:
|
|
390
|
+
# Move forward / backward
|
|
391
|
+
self._speed[0] += 0.5 * event.delta[1]
|
|
392
|
+
elif keys.SHIFT in event.mouse_event.modifiers:
|
|
393
|
+
# Speed
|
|
394
|
+
s = 1.1 ** - event.delta[1]
|
|
395
|
+
self.scale_factor /= s # divide instead of multiply
|
|
396
|
+
print('scale factor: %1.1f units/s' % self.scale_factor)
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
if event.type == 'mouse_press':
|
|
400
|
+
event.handled = True
|
|
401
|
+
|
|
402
|
+
if event.type == 'mouse_release':
|
|
403
|
+
# Reset
|
|
404
|
+
self._event_value = None
|
|
405
|
+
# Apply rotation
|
|
406
|
+
self._rotation1 = (self._rotation2 * self._rotation1).normalize()
|
|
407
|
+
self._rotation2 = Quaternion()
|
|
408
|
+
event.handled = True
|
|
409
|
+
elif not self._timer.running:
|
|
410
|
+
# Ensure the timer runs
|
|
411
|
+
self._timer.start()
|
|
412
|
+
|
|
413
|
+
if event.type == 'mouse_move':
|
|
414
|
+
|
|
415
|
+
if event.press_event is None:
|
|
416
|
+
return
|
|
417
|
+
if not event.buttons:
|
|
418
|
+
return
|
|
419
|
+
|
|
420
|
+
# Prepare
|
|
421
|
+
modifiers = event.mouse_event.modifiers
|
|
422
|
+
pos1 = event.mouse_event.press_event.pos
|
|
423
|
+
pos2 = event.mouse_event.pos
|
|
424
|
+
w, h = self._viewbox.size
|
|
425
|
+
|
|
426
|
+
if 1 in event.buttons and not modifiers:
|
|
427
|
+
# rotate
|
|
428
|
+
|
|
429
|
+
# get normalized delta values
|
|
430
|
+
d_az = -float(pos2[0] - pos1[0]) / w
|
|
431
|
+
d_el = +float(pos2[1] - pos1[1]) / h
|
|
432
|
+
# Apply gain
|
|
433
|
+
d_az *= - 0.5 * math.pi # * self._speed_rot
|
|
434
|
+
d_el *= + 0.5 * math.pi # * self._speed_rot
|
|
435
|
+
# Create temporary quaternions
|
|
436
|
+
q_az = Quaternion.create_from_axis_angle(d_az, 0, 1, 0)
|
|
437
|
+
q_el = Quaternion.create_from_axis_angle(d_el, 1, 0, 0)
|
|
438
|
+
|
|
439
|
+
# Apply to global quaternion
|
|
440
|
+
self._rotation2 = (q_el.normalize() * q_az).normalize()
|
|
441
|
+
event.handled = True
|
|
442
|
+
|
|
443
|
+
elif 2 in event.buttons and keys.CONTROL in modifiers:
|
|
444
|
+
# zoom --> fov
|
|
445
|
+
if self._event_value is None:
|
|
446
|
+
self._event_value = self._fov
|
|
447
|
+
p1 = np.array(event.press_event.pos)[:2]
|
|
448
|
+
p2 = np.array(event.pos)[:2]
|
|
449
|
+
p1c = event.map_to_canvas(p1)[:2]
|
|
450
|
+
p2c = event.map_to_canvas(p2)[:2]
|
|
451
|
+
d = p2c - p1c
|
|
452
|
+
fov = self._event_value * math.exp(-0.01*d[1])
|
|
453
|
+
self._fov = min(90.0, max(10, fov))
|
|
454
|
+
event.handled = True
|
|
455
|
+
|
|
456
|
+
# Make transform be updated on the next timer tick.
|
|
457
|
+
# By doing it at timer tick, we avoid shaky behavior
|
|
458
|
+
self._update_from_mouse = True
|
|
459
|
+
|
|
460
|
+
def _update_projection_transform(self, fx, fy):
|
|
461
|
+
PerspectiveCamera._update_projection_transform(self, fx, fy)
|
|
462
|
+
|
|
463
|
+
# Turn our internal quaternion representation into rotation
|
|
464
|
+
# of our transform
|
|
465
|
+
|
|
466
|
+
axis_angle = self.rotation.get_axis_angle()
|
|
467
|
+
angle = axis_angle[0] * 180 / math.pi
|
|
468
|
+
|
|
469
|
+
tr = self.transform
|
|
470
|
+
tr.reset()
|
|
471
|
+
#
|
|
472
|
+
tr.rotate(-angle, axis_angle[1:])
|
|
473
|
+
tr.scale([1.0/a for a in self._flip_factors])
|
|
474
|
+
tr.translate(self._center)
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
from __future__ import division
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from .panzoom import PanZoomCamera
|
|
9
|
+
from ...visuals.transforms.nonlinear import (MagnifyTransform,
|
|
10
|
+
Magnify1DTransform)
|
|
11
|
+
from ...app import Timer
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MagnifyCamera(PanZoomCamera):
|
|
15
|
+
"""Camera implementing a MagnifyTransform combined with PanZoomCamera.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
size_factor : float
|
|
20
|
+
The size factor to use.
|
|
21
|
+
radius_ratio : float
|
|
22
|
+
The radius ratio to use.
|
|
23
|
+
**kwargs : dict
|
|
24
|
+
Keyword arguments to pass to `PanZoomCamera` and create a transform.
|
|
25
|
+
|
|
26
|
+
Notes
|
|
27
|
+
-----
|
|
28
|
+
This Camera uses the mouse cursor position to set the center position of
|
|
29
|
+
the MagnifyTransform, and uses mouse wheel events to adjust the
|
|
30
|
+
magnification factor.
|
|
31
|
+
|
|
32
|
+
At high magnification, very small mouse movements can result in large
|
|
33
|
+
changes, so we use a timer to animate transitions in the transform
|
|
34
|
+
properties.
|
|
35
|
+
|
|
36
|
+
The camera also adjusts the size of its "lens" area when the view is
|
|
37
|
+
resized.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
transform_class = MagnifyTransform
|
|
41
|
+
|
|
42
|
+
def __init__(self, size_factor=0.25, radius_ratio=0.9, **kwargs):
|
|
43
|
+
# what fraction of the view width to use for radius
|
|
44
|
+
self.size_factor = size_factor
|
|
45
|
+
|
|
46
|
+
# ratio of inner to outer lens radius
|
|
47
|
+
self.radius_ratio = radius_ratio
|
|
48
|
+
|
|
49
|
+
# Extract kwargs for panzoom
|
|
50
|
+
camkwargs = {}
|
|
51
|
+
for key in ('parent', 'name', 'rect', 'aspect'):
|
|
52
|
+
if key in kwargs:
|
|
53
|
+
camkwargs[key] = kwargs.pop(key)
|
|
54
|
+
|
|
55
|
+
# Create the mag transform - kwrds go here
|
|
56
|
+
self.mag = self.transform_class(**kwargs)
|
|
57
|
+
|
|
58
|
+
# for handling smooth transitions
|
|
59
|
+
self.mag_target = self.mag.mag
|
|
60
|
+
self.mag._mag = self.mag_target
|
|
61
|
+
self.mouse_pos = None
|
|
62
|
+
self.timer = Timer(interval=0.016, connect=self.on_timer)
|
|
63
|
+
|
|
64
|
+
super(MagnifyCamera, self).__init__(**camkwargs)
|
|
65
|
+
|
|
66
|
+
# This tells the camera to insert the magnification transform at the
|
|
67
|
+
# beginning of the transform it applies to the scene. This is the
|
|
68
|
+
# correct place for the mag transform because:
|
|
69
|
+
# 1. We want it to apply to everything inside the scene, and not to
|
|
70
|
+
# the ViewBox itself or anything outside of the ViewBox.
|
|
71
|
+
# 2. We do _not_ want the pan/zoom transforms applied first, because
|
|
72
|
+
# the scale factors implemented there should not change the shape
|
|
73
|
+
# of the lens.
|
|
74
|
+
self.pre_transform = self.mag
|
|
75
|
+
|
|
76
|
+
def _viewbox_set(self, viewbox):
|
|
77
|
+
PanZoomCamera._viewbox_set(self, viewbox)
|
|
78
|
+
|
|
79
|
+
def _viewbox_unset(self, viewbox):
|
|
80
|
+
PanZoomCamera._viewbox_unset(self, viewbox)
|
|
81
|
+
self.timer.stop()
|
|
82
|
+
|
|
83
|
+
def viewbox_mouse_event(self, event):
|
|
84
|
+
"""The ViewBox mouse event handler
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
event : instance of Event
|
|
89
|
+
The mouse event.
|
|
90
|
+
"""
|
|
91
|
+
# When the attached ViewBox reseives a mouse event, it is sent to the
|
|
92
|
+
# camera here.
|
|
93
|
+
|
|
94
|
+
self.mouse_pos = event.pos[:2]
|
|
95
|
+
if event.type == 'mouse_wheel':
|
|
96
|
+
# wheel rolled; adjust the magnification factor and hide the
|
|
97
|
+
# event from the superclass
|
|
98
|
+
m = self.mag_target
|
|
99
|
+
m *= 1.2 ** event.delta[1]
|
|
100
|
+
m = m if m > 1 else 1
|
|
101
|
+
self.mag_target = m
|
|
102
|
+
else:
|
|
103
|
+
# send everything _except_ wheel events to the superclass
|
|
104
|
+
super(MagnifyCamera, self).viewbox_mouse_event(event)
|
|
105
|
+
|
|
106
|
+
# start the timer to smoothly modify the transform properties.
|
|
107
|
+
if not self.timer.running:
|
|
108
|
+
self.timer.start()
|
|
109
|
+
|
|
110
|
+
self._update_transform()
|
|
111
|
+
|
|
112
|
+
def on_timer(self, event=None):
|
|
113
|
+
"""Timer event handler
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
event : instance of Event
|
|
118
|
+
The timer event.
|
|
119
|
+
"""
|
|
120
|
+
# Smoothly update center and magnification properties of the transform
|
|
121
|
+
k = np.clip(100. / self.mag.mag, 10, 100)
|
|
122
|
+
s = 10**(-k * event.dt)
|
|
123
|
+
|
|
124
|
+
c = np.array(self.mag.center)
|
|
125
|
+
c1 = c * s + self.mouse_pos * (1-s)
|
|
126
|
+
|
|
127
|
+
m = self.mag.mag * s + self.mag_target * (1-s)
|
|
128
|
+
|
|
129
|
+
# If changes are very small, then it is safe to stop the timer.
|
|
130
|
+
if (np.all(np.abs((c - c1) / c1) < 1e-5) and
|
|
131
|
+
(np.abs(np.log(m / self.mag.mag)) < 1e-3)):
|
|
132
|
+
self.timer.stop()
|
|
133
|
+
|
|
134
|
+
self.mag.center = c1
|
|
135
|
+
self.mag.mag = m
|
|
136
|
+
|
|
137
|
+
self._update_transform()
|
|
138
|
+
|
|
139
|
+
def viewbox_resize_event(self, event):
|
|
140
|
+
"""The ViewBox resize event handler
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
event : instance of Event
|
|
145
|
+
The viewbox resize event.
|
|
146
|
+
"""
|
|
147
|
+
PanZoomCamera.viewbox_resize_event(self, event)
|
|
148
|
+
self.view_changed()
|
|
149
|
+
|
|
150
|
+
def view_changed(self):
|
|
151
|
+
# make sure radii are updated when a view is attached.
|
|
152
|
+
# when the view resizes, we change the lens radii to match.
|
|
153
|
+
if self._viewbox is not None:
|
|
154
|
+
vbs = self._viewbox.size
|
|
155
|
+
r = min(vbs) * self.size_factor
|
|
156
|
+
self.mag.radii = r * self.radius_ratio, r
|
|
157
|
+
|
|
158
|
+
PanZoomCamera.view_changed(self)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class Magnify1DCamera(MagnifyCamera):
|
|
162
|
+
transform_class = Magnify1DTransform
|
|
163
|
+
__doc__ = MagnifyCamera.__doc__
|