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/io/image.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# -----------------------------------------------------------------------------
|
|
3
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
4
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
# Author: Luke Campagnola
|
|
7
|
+
# -----------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
import struct
|
|
11
|
+
import zlib
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _make_png(data, level=6):
|
|
16
|
+
"""Convert numpy array to PNG byte array.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
data : numpy.ndarray
|
|
21
|
+
Data must be (H, W, 3 | 4) with dtype = np.ubyte (np.uint8)
|
|
22
|
+
level : int
|
|
23
|
+
https://docs.python.org/2/library/zlib.html#zlib.compress
|
|
24
|
+
An integer from 0 to 9 controlling the level of compression:
|
|
25
|
+
|
|
26
|
+
* 1 is fastest and produces the least compression,
|
|
27
|
+
* 9 is slowest and produces the most.
|
|
28
|
+
* 0 is no compression.
|
|
29
|
+
|
|
30
|
+
The default value is 6.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
png : array
|
|
35
|
+
PNG formatted array
|
|
36
|
+
"""
|
|
37
|
+
# Eventually we might want to use ext/png.py for this, but this
|
|
38
|
+
# routine *should* be faster b/c it's specialized for our use case
|
|
39
|
+
|
|
40
|
+
def mkchunk(data, name):
|
|
41
|
+
if isinstance(data, np.ndarray):
|
|
42
|
+
size = data.nbytes
|
|
43
|
+
else:
|
|
44
|
+
size = len(data)
|
|
45
|
+
chunk = np.empty(size + 12, dtype=np.ubyte)
|
|
46
|
+
chunk.data[0:4] = np.array(size, '>u4').tobytes()
|
|
47
|
+
chunk.data[4:8] = name.encode('ASCII')
|
|
48
|
+
chunk.data[8:8 + size] = data
|
|
49
|
+
# and-ing may not be necessary, but is done for safety:
|
|
50
|
+
# https://docs.python.org/3/library/zlib.html#zlib.crc32
|
|
51
|
+
chunk.data[-4:] = np.array(zlib.crc32(chunk[4:-4]) & 0xffffffff,
|
|
52
|
+
'>u4').tobytes()
|
|
53
|
+
return chunk
|
|
54
|
+
|
|
55
|
+
if data.dtype != np.ubyte:
|
|
56
|
+
raise TypeError('data.dtype must be np.ubyte (np.uint8)')
|
|
57
|
+
|
|
58
|
+
dim = data.shape[2] # Dimension
|
|
59
|
+
if dim not in (3, 4):
|
|
60
|
+
raise TypeError('data.shape[2] must be in (3, 4)')
|
|
61
|
+
|
|
62
|
+
# www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
|
|
63
|
+
if dim == 4:
|
|
64
|
+
ctyp = 0b0110 # RGBA
|
|
65
|
+
else:
|
|
66
|
+
ctyp = 0b0010 # RGB
|
|
67
|
+
|
|
68
|
+
# www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
|
|
69
|
+
header = b'\x89PNG\x0d\x0a\x1a\x0a' # header
|
|
70
|
+
|
|
71
|
+
h, w = data.shape[:2]
|
|
72
|
+
depth = data.itemsize * 8
|
|
73
|
+
ihdr = struct.pack('!IIBBBBB', w, h, depth, ctyp, 0, 0, 0)
|
|
74
|
+
c1 = mkchunk(ihdr, 'IHDR')
|
|
75
|
+
|
|
76
|
+
# www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IDAT
|
|
77
|
+
# insert filter byte at each scanline
|
|
78
|
+
idat = np.empty((h, w * dim + 1), dtype=np.ubyte)
|
|
79
|
+
idat[:, 1:] = data.reshape(h, w * dim)
|
|
80
|
+
idat[:, 0] = 0
|
|
81
|
+
|
|
82
|
+
comp_data = zlib.compress(idat, level)
|
|
83
|
+
c2 = mkchunk(comp_data, 'IDAT')
|
|
84
|
+
c3 = mkchunk(np.empty((0,), dtype=np.ubyte), 'IEND')
|
|
85
|
+
|
|
86
|
+
# concatenate
|
|
87
|
+
lh = len(header)
|
|
88
|
+
png = np.empty(lh + c1.nbytes + c2.nbytes + c3.nbytes, dtype=np.ubyte)
|
|
89
|
+
png.data[:lh] = header
|
|
90
|
+
p = lh
|
|
91
|
+
|
|
92
|
+
for chunk in (c1, c2, c3):
|
|
93
|
+
png[p:p + len(chunk)] = chunk
|
|
94
|
+
p += chunk.nbytes
|
|
95
|
+
|
|
96
|
+
return png
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def read_png(filename):
|
|
100
|
+
"""Read a PNG file to RGB8 or RGBA8
|
|
101
|
+
Requires Pillow.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
filename : str
|
|
106
|
+
File to read.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
data : array
|
|
111
|
+
Image data.
|
|
112
|
+
|
|
113
|
+
See also
|
|
114
|
+
--------
|
|
115
|
+
write_png, imread, imsave
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
from PIL import Image
|
|
119
|
+
x = Image.open(filename)
|
|
120
|
+
try:
|
|
121
|
+
y = np.asarray(x)
|
|
122
|
+
y = np.array([yy for yy in y], np.uint8)
|
|
123
|
+
finally:
|
|
124
|
+
x.close()
|
|
125
|
+
return y
|
|
126
|
+
except ImportError:
|
|
127
|
+
raise RuntimeError("read_png requires the Pillow package.")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def write_png(filename, data):
|
|
131
|
+
"""Write a PNG file
|
|
132
|
+
|
|
133
|
+
Unlike imsave, this requires no external dependencies.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
filename : str
|
|
138
|
+
File to save to.
|
|
139
|
+
data : array
|
|
140
|
+
Image data.
|
|
141
|
+
|
|
142
|
+
See also
|
|
143
|
+
--------
|
|
144
|
+
read_png, imread, imsave
|
|
145
|
+
"""
|
|
146
|
+
data = np.asarray(data)
|
|
147
|
+
if not data.ndim == 3 and data.shape[-1] in (3, 4):
|
|
148
|
+
raise ValueError('data must be a 3D array with last dimension 3 or 4')
|
|
149
|
+
with open(filename, 'wb') as f:
|
|
150
|
+
f.write(_make_png(data)) # Save array with make_png
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def imread(filename, format=None):
|
|
154
|
+
"""Read image data from disk
|
|
155
|
+
|
|
156
|
+
Requires imageio or PIL.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
filename : str
|
|
161
|
+
Filename to read.
|
|
162
|
+
format : str | None
|
|
163
|
+
Format of the file. If None, it will be inferred from the filename.
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
data : array
|
|
168
|
+
Image data.
|
|
169
|
+
|
|
170
|
+
See also
|
|
171
|
+
--------
|
|
172
|
+
imsave, read_png, write_png
|
|
173
|
+
"""
|
|
174
|
+
imageio, PIL = _check_img_lib()
|
|
175
|
+
if imageio is not None:
|
|
176
|
+
return imageio.imread(filename, format)
|
|
177
|
+
elif PIL is not None:
|
|
178
|
+
im = PIL.Image.open(filename)
|
|
179
|
+
if im.mode == 'P':
|
|
180
|
+
im = im.convert()
|
|
181
|
+
# Make numpy array
|
|
182
|
+
a = np.asarray(im)
|
|
183
|
+
if len(a.shape) == 0:
|
|
184
|
+
raise MemoryError("Too little memory to convert PIL image to "
|
|
185
|
+
"array")
|
|
186
|
+
return a
|
|
187
|
+
else:
|
|
188
|
+
raise RuntimeError("imread requires the imageio or PIL package.")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def imsave(filename, im, format=None):
|
|
192
|
+
"""Save image data to disk
|
|
193
|
+
|
|
194
|
+
Requires imageio or PIL.
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
filename : str
|
|
199
|
+
Filename to write.
|
|
200
|
+
im : array
|
|
201
|
+
Image data.
|
|
202
|
+
format : str | None
|
|
203
|
+
Format of the file. If None, it will be inferred from the filename.
|
|
204
|
+
|
|
205
|
+
See also
|
|
206
|
+
--------
|
|
207
|
+
imread, read_png, write_png
|
|
208
|
+
"""
|
|
209
|
+
# Import imageio or PIL
|
|
210
|
+
imageio, PIL = _check_img_lib()
|
|
211
|
+
if imageio is not None:
|
|
212
|
+
return imageio.imsave(filename, im, format)
|
|
213
|
+
elif PIL is not None:
|
|
214
|
+
pim = PIL.Image.fromarray(im)
|
|
215
|
+
pim.save(filename, format)
|
|
216
|
+
else:
|
|
217
|
+
raise RuntimeError("imsave requires the imageio or PIL package.")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _check_img_lib():
|
|
221
|
+
"""Utility to search for imageio or PIL"""
|
|
222
|
+
# Import imageio or PIL
|
|
223
|
+
imageio = PIL = None
|
|
224
|
+
try:
|
|
225
|
+
import imageio
|
|
226
|
+
except ImportError:
|
|
227
|
+
try:
|
|
228
|
+
import PIL.Image
|
|
229
|
+
except ImportError:
|
|
230
|
+
pass
|
|
231
|
+
return imageio, PIL
|
vispy/io/mesh.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
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
|
+
"""Reading and writing of data like images and meshes."""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from os import path as op
|
|
9
|
+
|
|
10
|
+
from .wavefront import WavefrontReader, WavefrontWriter
|
|
11
|
+
from .stl import load_stl
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_mesh(fname):
|
|
15
|
+
"""Read mesh data from file.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
fname : str
|
|
20
|
+
File name to read. Format will be inferred from the filename.
|
|
21
|
+
Currently only '.obj' and '.obj.gz' are supported.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
vertices : array
|
|
26
|
+
Vertices.
|
|
27
|
+
faces : array | None
|
|
28
|
+
Triangle face definitions.
|
|
29
|
+
normals : array
|
|
30
|
+
Normals for the mesh.
|
|
31
|
+
texcoords : array | None
|
|
32
|
+
Texture coordinates.
|
|
33
|
+
"""
|
|
34
|
+
# Check format
|
|
35
|
+
fmt = op.splitext(fname)[1].lower()
|
|
36
|
+
if fmt == '.gz':
|
|
37
|
+
fmt = op.splitext(op.splitext(fname)[0])[1].lower()
|
|
38
|
+
|
|
39
|
+
if fmt in ('.obj'):
|
|
40
|
+
return WavefrontReader.read(fname)
|
|
41
|
+
elif fmt in ('.stl'):
|
|
42
|
+
file_obj = open(fname, mode='rb')
|
|
43
|
+
mesh = load_stl(file_obj)
|
|
44
|
+
vertices = mesh['vertices']
|
|
45
|
+
faces = mesh['faces']
|
|
46
|
+
normals = mesh['face_normals']
|
|
47
|
+
texcoords = None
|
|
48
|
+
return vertices, faces, normals, texcoords
|
|
49
|
+
else:
|
|
50
|
+
try:
|
|
51
|
+
import meshio
|
|
52
|
+
except ImportError:
|
|
53
|
+
raise ValueError('read_mesh does not understand format %s.' % fmt)
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
mesh = meshio.read(fname)
|
|
57
|
+
except meshio.ReadError:
|
|
58
|
+
raise ValueError('read_mesh does not understand format %s.' % fmt)
|
|
59
|
+
|
|
60
|
+
triangles = mesh.get_cells_type("triangle")
|
|
61
|
+
if len(triangles) == 0:
|
|
62
|
+
raise ValueError('mesh file does not contain triangles.')
|
|
63
|
+
|
|
64
|
+
return mesh.points, triangles, None, None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def write_mesh(fname, vertices, faces, normals, texcoords, name='',
|
|
68
|
+
format=None, overwrite=False, reshape_faces=True):
|
|
69
|
+
"""Write mesh data to file.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
fname : str
|
|
74
|
+
Filename to write. Must end with ".obj" or ".gz".
|
|
75
|
+
vertices : array
|
|
76
|
+
Vertices.
|
|
77
|
+
faces : array | None
|
|
78
|
+
Triangle face definitions.
|
|
79
|
+
normals : array
|
|
80
|
+
Normals for the mesh.
|
|
81
|
+
texcoords : array | None
|
|
82
|
+
Texture coordinates.
|
|
83
|
+
name : str
|
|
84
|
+
Name of the object.
|
|
85
|
+
format : str
|
|
86
|
+
Currently only "obj" is supported.
|
|
87
|
+
overwrite : bool
|
|
88
|
+
If the file exists, overwrite it.
|
|
89
|
+
reshape_faces : bool
|
|
90
|
+
Reshape the `faces` array to (Nf, 3). Set to `False`
|
|
91
|
+
if you need to write a mesh with non triangular faces.
|
|
92
|
+
"""
|
|
93
|
+
# Check file
|
|
94
|
+
if op.isfile(fname) and not overwrite:
|
|
95
|
+
raise IOError('file "%s" exists, use overwrite=True' % fname)
|
|
96
|
+
|
|
97
|
+
if format is None:
|
|
98
|
+
format = os.path.splitext(fname)[1][1:]
|
|
99
|
+
|
|
100
|
+
# Check format
|
|
101
|
+
if format == 'obj':
|
|
102
|
+
WavefrontWriter.write(fname, vertices, faces,
|
|
103
|
+
normals, texcoords, name, reshape_faces)
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
import meshio
|
|
108
|
+
except ImportError:
|
|
109
|
+
raise ValueError('write_mesh does not understand format %s.' % format)
|
|
110
|
+
|
|
111
|
+
cell_data = {}
|
|
112
|
+
if normals is not None:
|
|
113
|
+
cell_data["normals"] = [normals]
|
|
114
|
+
if texcoords is not None:
|
|
115
|
+
cell_data["texcoords"] = [texcoords]
|
|
116
|
+
|
|
117
|
+
mesh = meshio.Mesh(vertices, [("triangle", faces)], cell_data=cell_data)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
mesh.write(fname, file_format=format)
|
|
121
|
+
except meshio.WriteError:
|
|
122
|
+
raise ValueError('write_mesh does not understand format %s.' % format)
|
vispy/io/stl.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) 2015 Michael Dawson-Haggerty
|
|
3
|
+
# Distributed under the MIT License.
|
|
4
|
+
# Copied from the trimesh project.
|
|
5
|
+
# See https://github.com/mikedh/trimesh for more information.
|
|
6
|
+
# See https://github.com/mikedh/trimesh/blob/master/LICENSE.md for
|
|
7
|
+
# the license.
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HeaderError(Exception):
|
|
13
|
+
# the exception raised if an STL file object doesn't match its header
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# define a numpy datatype for the data section of a binary STL file
|
|
18
|
+
_stl_dtype = np.dtype([('normals', np.float32, (3)),
|
|
19
|
+
('vertices', np.float32, (3, 3)),
|
|
20
|
+
('attributes', np.uint16)])
|
|
21
|
+
# define a numpy datatype for the header of a binary STL file
|
|
22
|
+
_stl_dtype_header = np.dtype([('header', np.void, 80),
|
|
23
|
+
('face_count', np.int32)])
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def load_stl(file_obj, file_type=None):
|
|
27
|
+
"""
|
|
28
|
+
Load an STL file from a file object.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
file_obj: open file- like object
|
|
33
|
+
file_type: not used
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
loaded: kwargs for a Trimesh constructor with keys:
|
|
38
|
+
vertices: (n,3) float, vertices
|
|
39
|
+
faces: (m,3) int, indexes of vertices
|
|
40
|
+
face_normals: (m,3) float, normal vector of each face
|
|
41
|
+
"""
|
|
42
|
+
# save start of file obj
|
|
43
|
+
file_pos = file_obj.tell()
|
|
44
|
+
try:
|
|
45
|
+
# check the file for a header which matches the file length
|
|
46
|
+
# if that is true, it is almost certainly a binary STL file
|
|
47
|
+
# if the header doesn't match the file length a HeaderError will be
|
|
48
|
+
# raised
|
|
49
|
+
return load_stl_binary(file_obj)
|
|
50
|
+
except HeaderError:
|
|
51
|
+
# move the file back to where it was initially
|
|
52
|
+
file_obj.seek(file_pos)
|
|
53
|
+
# try to load the file as an ASCII STL
|
|
54
|
+
# if the header doesn't match the file length a HeaderError will be
|
|
55
|
+
# raised
|
|
56
|
+
return load_stl_ascii(file_obj)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def load_stl_binary(file_obj):
|
|
60
|
+
"""
|
|
61
|
+
Load a binary STL file from a file object.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
file_obj: open file- like object
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
loaded: kwargs for a Trimesh constructor with keys:
|
|
70
|
+
vertices: (n,3) float, vertices
|
|
71
|
+
faces: (m,3) int, indexes of vertices
|
|
72
|
+
face_normals: (m,3) float, normal vector of each face
|
|
73
|
+
"""
|
|
74
|
+
# the header is always 84 bytes long, we just reference the dtype.itemsize
|
|
75
|
+
# to be explicit about where that magical number comes from
|
|
76
|
+
header_length = _stl_dtype_header.itemsize
|
|
77
|
+
header_data = file_obj.read(header_length)
|
|
78
|
+
if len(header_data) < header_length:
|
|
79
|
+
raise HeaderError('Binary STL file not long enough to contain header!')
|
|
80
|
+
|
|
81
|
+
header = np.fromstring(header_data, dtype=_stl_dtype_header)
|
|
82
|
+
|
|
83
|
+
# now we check the length from the header versus the length of the file
|
|
84
|
+
# data_start should always be position 84, but hard coding that felt ugly
|
|
85
|
+
data_start = file_obj.tell()
|
|
86
|
+
# this seeks to the end of the file
|
|
87
|
+
# position 0, relative to the end of the file 'whence=2'
|
|
88
|
+
file_obj.seek(0, 2)
|
|
89
|
+
# we save the location of the end of the file and seek back to where we
|
|
90
|
+
# started from
|
|
91
|
+
data_end = file_obj.tell()
|
|
92
|
+
file_obj.seek(data_start)
|
|
93
|
+
|
|
94
|
+
# the binary format has a rigidly defined structure, and if the length
|
|
95
|
+
# of the file doesn't match the header, the loaded version is almost
|
|
96
|
+
# certainly going to be garbage.
|
|
97
|
+
len_data = data_end - data_start
|
|
98
|
+
len_expected = header['face_count'] * _stl_dtype.itemsize
|
|
99
|
+
|
|
100
|
+
# this check is to see if this really is a binary STL file.
|
|
101
|
+
# if we don't do this and try to load a file that isn't structured properly
|
|
102
|
+
# we will be producing garbage or crashing hard
|
|
103
|
+
# so it's much better to raise an exception here.
|
|
104
|
+
if len_data != len_expected:
|
|
105
|
+
raise HeaderError('Binary STL has incorrect length in header!')
|
|
106
|
+
|
|
107
|
+
# all of our vertices will be loaded in order due to the STL format,
|
|
108
|
+
# so faces are just sequential indices reshaped.
|
|
109
|
+
faces = np.arange(header['face_count'] * 3).reshape((-1, 3))
|
|
110
|
+
blob = np.fromstring(file_obj.read(), dtype=_stl_dtype)
|
|
111
|
+
|
|
112
|
+
result = {'vertices': blob['vertices'].reshape((-1, 3)),
|
|
113
|
+
'face_normals': blob['normals'].reshape((-1, 3)),
|
|
114
|
+
'faces': faces}
|
|
115
|
+
return result
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def load_stl_ascii(file_obj):
|
|
119
|
+
"""
|
|
120
|
+
Load an ASCII STL file from a file object.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
file_obj: open file- like object
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
loaded: kwargs for a Trimesh constructor with keys:
|
|
129
|
+
vertices: (n,3) float, vertices
|
|
130
|
+
faces: (m,3) int, indexes of vertices
|
|
131
|
+
face_normals: (m,3) float, normal vector of each face
|
|
132
|
+
"""
|
|
133
|
+
# header (not used by this function)
|
|
134
|
+
file_obj.readline()
|
|
135
|
+
|
|
136
|
+
text = file_obj.read()
|
|
137
|
+
if hasattr(text, 'decode'):
|
|
138
|
+
text = text.decode('utf-8')
|
|
139
|
+
text = text.lower().split('endsolid')[0]
|
|
140
|
+
blob = np.array(text.split())
|
|
141
|
+
|
|
142
|
+
# there are 21 'words' in each face
|
|
143
|
+
face_len = 21
|
|
144
|
+
face_count = len(blob) / face_len
|
|
145
|
+
if (len(blob) % face_len) != 0:
|
|
146
|
+
raise HeaderError('Incorrect number of values in STL file!')
|
|
147
|
+
|
|
148
|
+
face_count = int(face_count)
|
|
149
|
+
# this offset is to be added to a fixed set of indices that is tiled
|
|
150
|
+
offset = face_len * np.arange(face_count).reshape((-1, 1))
|
|
151
|
+
normal_index = np.tile([2, 3, 4], (face_count, 1)) + offset
|
|
152
|
+
vertex_index = np.tile(
|
|
153
|
+
[8, 9, 10, 12, 13, 14, 16, 17, 18], (face_count, 1)) + offset
|
|
154
|
+
|
|
155
|
+
# faces are groups of three sequential vertices, as vertices are not
|
|
156
|
+
# references
|
|
157
|
+
faces = np.arange(face_count * 3).reshape((-1, 3))
|
|
158
|
+
face_normals = blob[normal_index].astype(np.float64)
|
|
159
|
+
vertices = blob[vertex_index.reshape((-1, 3))].astype(np.float64)
|
|
160
|
+
|
|
161
|
+
return {'vertices': vertices,
|
|
162
|
+
'faces': faces,
|
|
163
|
+
'face_normals': face_normals}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
_stl_loaders = {'stl': load_stl,
|
|
167
|
+
'stl_ascii': load_stl}
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
|
+
import numpy as np
|
|
5
|
+
from numpy.testing import assert_array_equal, assert_allclose
|
|
6
|
+
from os import path as op
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
from vispy.io import load_crate, imsave, imread, read_png, write_png
|
|
10
|
+
from vispy.testing import requires_img_lib, run_tests_if_main
|
|
11
|
+
from vispy.util import _TempDir
|
|
12
|
+
|
|
13
|
+
temp_dir = _TempDir()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_make_png():
|
|
17
|
+
"""Test to ensure that make_png functions correctly."""
|
|
18
|
+
# Save random RGBA and RGB arrays onto disk as PNGs using make_png.
|
|
19
|
+
# Read them back with an image library and check whether the array
|
|
20
|
+
# saved is equal to the array read.
|
|
21
|
+
|
|
22
|
+
# Create random RGBA array as type ubyte
|
|
23
|
+
rgba_save = np.random.randint(256, size=(100, 100, 4)).astype(np.ubyte)
|
|
24
|
+
# Get rid of the alpha for RGB
|
|
25
|
+
rgb_save = rgba_save[:, :, :3]
|
|
26
|
+
# Output file should be in temp
|
|
27
|
+
png_out = op.join(temp_dir, 'random.png')
|
|
28
|
+
|
|
29
|
+
# write_png implicitly tests _make_png
|
|
30
|
+
for rgb_a in (rgba_save, rgb_save):
|
|
31
|
+
write_png(png_out, rgb_a)
|
|
32
|
+
rgb_a_read = read_png(png_out)
|
|
33
|
+
assert_array_equal(rgb_a, rgb_a_read)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@requires_img_lib()
|
|
37
|
+
def test_read_write_image():
|
|
38
|
+
"""Test reading and writing of images"""
|
|
39
|
+
fname = op.join(temp_dir, 'out.png')
|
|
40
|
+
im1 = load_crate()
|
|
41
|
+
imsave(fname, im1, format='png')
|
|
42
|
+
with warnings.catch_warnings(record=True): # PIL unclosed file
|
|
43
|
+
im2 = imread(fname)
|
|
44
|
+
assert_allclose(im1, im2)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
run_tests_if_main()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
|
+
import numpy as np
|
|
5
|
+
from os import path as op
|
|
6
|
+
from numpy.testing import assert_allclose, assert_array_equal
|
|
7
|
+
|
|
8
|
+
from vispy.io import write_mesh, read_mesh, load_data_file
|
|
9
|
+
from vispy.geometry import _fast_cross_3d
|
|
10
|
+
from vispy.util import _TempDir
|
|
11
|
+
from vispy.testing import (run_tests_if_main, assert_equal, assert_raises,
|
|
12
|
+
requires_ssl)
|
|
13
|
+
|
|
14
|
+
temp_dir = _TempDir()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@requires_ssl()
|
|
18
|
+
def test_wavefront():
|
|
19
|
+
"""Test wavefront reader"""
|
|
20
|
+
fname_mesh = load_data_file('orig/triceratops.obj.gz')
|
|
21
|
+
fname_out = op.join(temp_dir, 'temp.obj')
|
|
22
|
+
mesh1 = read_mesh(fname_mesh)
|
|
23
|
+
assert_raises(IOError, read_mesh, 'foo.obj')
|
|
24
|
+
assert_raises(ValueError, read_mesh, op.abspath(__file__))
|
|
25
|
+
assert_raises(ValueError, write_mesh, fname_out, *mesh1, format='foo')
|
|
26
|
+
write_mesh(fname_out, mesh1[0], mesh1[1], mesh1[2], mesh1[3])
|
|
27
|
+
assert_raises(IOError, write_mesh, fname_out, *mesh1)
|
|
28
|
+
write_mesh(fname_out, *mesh1, overwrite=True)
|
|
29
|
+
mesh2 = read_mesh(fname_out)
|
|
30
|
+
assert_equal(len(mesh1), len(mesh2))
|
|
31
|
+
for m1, m2 in zip(mesh1, mesh2):
|
|
32
|
+
if m1 is None:
|
|
33
|
+
assert_equal(m2, None)
|
|
34
|
+
else:
|
|
35
|
+
assert_allclose(m1, m2, rtol=1e-5)
|
|
36
|
+
# test our efficient normal calculation routine
|
|
37
|
+
assert_allclose(mesh1[2], _slow_calculate_normals(mesh1[0], mesh1[1]),
|
|
38
|
+
rtol=1e-7, atol=1e-7)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_wavefront_non_triangular():
|
|
42
|
+
"""Test wavefront writing with non-triangular faces"""
|
|
43
|
+
vertices = np.array([[0.5, 1.375, 0.],
|
|
44
|
+
[0.5, 0.625, 0.],
|
|
45
|
+
[3.25, 1., 0.],
|
|
46
|
+
[1., 0.375, 0.],
|
|
47
|
+
[2., 0.375, 0.],
|
|
48
|
+
[1.5, 0.625, 0.],
|
|
49
|
+
[1.5, 1.375, 0.],
|
|
50
|
+
[1., 1.625, 0.],
|
|
51
|
+
[2., 1.625, 0.]])
|
|
52
|
+
|
|
53
|
+
faces = np.array([[1, 0, 7, 6, 5, 3],
|
|
54
|
+
[4, 5, 6, 8, 2]], dtype=object)
|
|
55
|
+
fname_out = op.join(temp_dir, 'temp.obj')
|
|
56
|
+
write_mesh(fname_out, vertices=vertices,
|
|
57
|
+
faces=faces, normals=None,
|
|
58
|
+
texcoords=None, overwrite=True,
|
|
59
|
+
reshape_faces=False)
|
|
60
|
+
assert_raises(RuntimeError, read_mesh, fname_out)
|
|
61
|
+
with open(fname_out, 'r+') as out_file:
|
|
62
|
+
lines = out_file.readlines()
|
|
63
|
+
assert lines[-1].startswith('f 5 6 7 9 3')
|
|
64
|
+
assert lines[-2].startswith('f 2 1 8 7 6 4')
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_meshio():
|
|
68
|
+
"""Test meshio i/o"""
|
|
69
|
+
vertices = np.array([[0.0, 0.0, 0.0],
|
|
70
|
+
[1.0, 0.0, 0.],
|
|
71
|
+
[-.0, 1.0, 0.],
|
|
72
|
+
[1.0, 1.0, 0.]])
|
|
73
|
+
|
|
74
|
+
faces = np.array([[0, 1, 3],
|
|
75
|
+
[1, 2, 3]])
|
|
76
|
+
fname_out = op.join(temp_dir, 'temp.vtk')
|
|
77
|
+
write_mesh(fname_out, vertices=vertices,
|
|
78
|
+
faces=faces, normals=None,
|
|
79
|
+
texcoords=None, overwrite=True,
|
|
80
|
+
reshape_faces=False)
|
|
81
|
+
out_vertices, out_faces, _, _ = read_mesh(fname_out)
|
|
82
|
+
|
|
83
|
+
assert np.all(np.abs(out_vertices - vertices) < 1.0e-14)
|
|
84
|
+
assert np.all(out_faces == faces)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _slow_calculate_normals(rr, tris):
|
|
88
|
+
"""Efficiently compute vertex normals for triangulated surface"""
|
|
89
|
+
# first, compute triangle normals
|
|
90
|
+
rr = rr.astype(np.float64)
|
|
91
|
+
r1 = rr[tris[:, 0], :]
|
|
92
|
+
r2 = rr[tris[:, 1], :]
|
|
93
|
+
r3 = rr[tris[:, 2], :]
|
|
94
|
+
tri_nn = np.cross((r2 - r1), (r3 - r1))
|
|
95
|
+
|
|
96
|
+
# Triangle normals and areas
|
|
97
|
+
size = np.sqrt(np.sum(tri_nn * tri_nn, axis=1))
|
|
98
|
+
zidx = np.where(size == 0)[0]
|
|
99
|
+
size[zidx] = 1.0 # prevent ugly divide-by-zero
|
|
100
|
+
tri_nn /= size[:, np.newaxis]
|
|
101
|
+
|
|
102
|
+
# accumulate the normals
|
|
103
|
+
nn = np.zeros((len(rr), 3))
|
|
104
|
+
for p, verts in enumerate(tris):
|
|
105
|
+
nn[verts] += tri_nn[p, :]
|
|
106
|
+
size = np.sqrt(np.sum(nn * nn, axis=1))
|
|
107
|
+
size[size == 0] = 1.0 # prevent ugly divide-by-zero
|
|
108
|
+
nn /= size[:, np.newaxis]
|
|
109
|
+
return nn
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_huge_cross():
|
|
113
|
+
"""Test cross product with lots of elements"""
|
|
114
|
+
x = np.random.rand(100000, 3)
|
|
115
|
+
y = np.random.rand(1, 3)
|
|
116
|
+
z = np.cross(x, y)
|
|
117
|
+
zz = _fast_cross_3d(x, y)
|
|
118
|
+
assert_array_equal(z, zz)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
run_tests_if_main()
|