vispy 0.14.0__cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vispy might be problematic. Click here for more details.

Files changed (519) hide show
  1. vispy/__init__.py +33 -0
  2. vispy/app/__init__.py +15 -0
  3. vispy/app/_default_app.py +76 -0
  4. vispy/app/_detect_eventloop.py +148 -0
  5. vispy/app/application.py +263 -0
  6. vispy/app/backends/__init__.py +52 -0
  7. vispy/app/backends/_egl.py +264 -0
  8. vispy/app/backends/_glfw.py +513 -0
  9. vispy/app/backends/_jupyter_rfb.py +278 -0
  10. vispy/app/backends/_offscreen_util.py +121 -0
  11. vispy/app/backends/_osmesa.py +235 -0
  12. vispy/app/backends/_pyglet.py +451 -0
  13. vispy/app/backends/_pyqt4.py +36 -0
  14. vispy/app/backends/_pyqt5.py +36 -0
  15. vispy/app/backends/_pyqt6.py +40 -0
  16. vispy/app/backends/_pyside.py +37 -0
  17. vispy/app/backends/_pyside2.py +52 -0
  18. vispy/app/backends/_pyside6.py +53 -0
  19. vispy/app/backends/_qt.py +968 -0
  20. vispy/app/backends/_sdl2.py +444 -0
  21. vispy/app/backends/_template.py +244 -0
  22. vispy/app/backends/_test.py +8 -0
  23. vispy/app/backends/_tk.py +800 -0
  24. vispy/app/backends/_wx.py +476 -0
  25. vispy/app/backends/tests/__init__.py +0 -0
  26. vispy/app/backends/tests/test_offscreen_util.py +52 -0
  27. vispy/app/backends/tests/test_rfb.py +77 -0
  28. vispy/app/base.py +294 -0
  29. vispy/app/canvas.py +828 -0
  30. vispy/app/qt.py +92 -0
  31. vispy/app/tests/__init__.py +0 -0
  32. vispy/app/tests/qt-designer.ui +58 -0
  33. vispy/app/tests/test_app.py +442 -0
  34. vispy/app/tests/test_backends.py +164 -0
  35. vispy/app/tests/test_canvas.py +122 -0
  36. vispy/app/tests/test_context.py +92 -0
  37. vispy/app/tests/test_qt.py +47 -0
  38. vispy/app/tests/test_simultaneous.py +134 -0
  39. vispy/app/timer.py +174 -0
  40. vispy/color/__init__.py +17 -0
  41. vispy/color/_color_dict.py +193 -0
  42. vispy/color/color_array.py +447 -0
  43. vispy/color/color_space.py +181 -0
  44. vispy/color/colormap.py +1134 -0
  45. vispy/color/tests/__init__.py +0 -0
  46. vispy/color/tests/test_color.py +352 -0
  47. vispy/conftest.py +12 -0
  48. vispy/ext/__init__.py +0 -0
  49. vispy/ext/cocoapy.py +1542 -0
  50. vispy/ext/cubehelix.py +138 -0
  51. vispy/ext/egl.py +375 -0
  52. vispy/ext/fontconfig.py +118 -0
  53. vispy/ext/gdi32plus.py +206 -0
  54. vispy/ext/osmesa.py +105 -0
  55. vispy/geometry/__init__.py +23 -0
  56. vispy/geometry/_triangulation_debugger.py +171 -0
  57. vispy/geometry/calculations.py +134 -0
  58. vispy/geometry/curves.py +399 -0
  59. vispy/geometry/generation.py +643 -0
  60. vispy/geometry/isocurve.py +175 -0
  61. vispy/geometry/isosurface.py +465 -0
  62. vispy/geometry/meshdata.py +698 -0
  63. vispy/geometry/normals.py +78 -0
  64. vispy/geometry/parametric.py +56 -0
  65. vispy/geometry/polygon.py +137 -0
  66. vispy/geometry/rect.py +210 -0
  67. vispy/geometry/tests/__init__.py +0 -0
  68. vispy/geometry/tests/test_calculations.py +23 -0
  69. vispy/geometry/tests/test_generation.py +56 -0
  70. vispy/geometry/tests/test_meshdata.py +106 -0
  71. vispy/geometry/tests/test_triangulation.py +506 -0
  72. vispy/geometry/torusknot.py +142 -0
  73. vispy/geometry/triangulation.py +876 -0
  74. vispy/gloo/__init__.py +56 -0
  75. vispy/gloo/buffer.py +505 -0
  76. vispy/gloo/context.py +272 -0
  77. vispy/gloo/framebuffer.py +257 -0
  78. vispy/gloo/gl/__init__.py +234 -0
  79. vispy/gloo/gl/_constants.py +332 -0
  80. vispy/gloo/gl/_es2.py +986 -0
  81. vispy/gloo/gl/_gl2.py +1365 -0
  82. vispy/gloo/gl/_proxy.py +499 -0
  83. vispy/gloo/gl/_pyopengl2.py +362 -0
  84. vispy/gloo/gl/dummy.py +24 -0
  85. vispy/gloo/gl/es2.py +62 -0
  86. vispy/gloo/gl/gl2.py +98 -0
  87. vispy/gloo/gl/glplus.py +168 -0
  88. vispy/gloo/gl/pyopengl2.py +97 -0
  89. vispy/gloo/gl/tests/__init__.py +0 -0
  90. vispy/gloo/gl/tests/test_basics.py +282 -0
  91. vispy/gloo/gl/tests/test_functionality.py +566 -0
  92. vispy/gloo/gl/tests/test_names.py +246 -0
  93. vispy/gloo/gl/tests/test_use.py +71 -0
  94. vispy/gloo/glir.py +1816 -0
  95. vispy/gloo/globject.py +101 -0
  96. vispy/gloo/preprocessor.py +67 -0
  97. vispy/gloo/program.py +543 -0
  98. vispy/gloo/tests/__init__.py +0 -0
  99. vispy/gloo/tests/test_buffer.py +558 -0
  100. vispy/gloo/tests/test_context.py +119 -0
  101. vispy/gloo/tests/test_framebuffer.py +195 -0
  102. vispy/gloo/tests/test_glir.py +307 -0
  103. vispy/gloo/tests/test_globject.py +35 -0
  104. vispy/gloo/tests/test_program.py +302 -0
  105. vispy/gloo/tests/test_texture.py +732 -0
  106. vispy/gloo/tests/test_use_gloo.py +187 -0
  107. vispy/gloo/tests/test_util.py +60 -0
  108. vispy/gloo/tests/test_wrappers.py +261 -0
  109. vispy/gloo/texture.py +1045 -0
  110. vispy/gloo/util.py +129 -0
  111. vispy/gloo/wrappers.py +762 -0
  112. vispy/glsl/__init__.py +42 -0
  113. vispy/glsl/antialias/antialias.glsl +7 -0
  114. vispy/glsl/antialias/cap-butt.glsl +31 -0
  115. vispy/glsl/antialias/cap-round.glsl +29 -0
  116. vispy/glsl/antialias/cap-square.glsl +30 -0
  117. vispy/glsl/antialias/cap-triangle-in.glsl +30 -0
  118. vispy/glsl/antialias/cap-triangle-out.glsl +30 -0
  119. vispy/glsl/antialias/cap.glsl +67 -0
  120. vispy/glsl/antialias/caps.glsl +67 -0
  121. vispy/glsl/antialias/filled.glsl +50 -0
  122. vispy/glsl/antialias/outline.glsl +40 -0
  123. vispy/glsl/antialias/stroke.glsl +43 -0
  124. vispy/glsl/arrowheads/angle.glsl +99 -0
  125. vispy/glsl/arrowheads/arrowheads.frag +60 -0
  126. vispy/glsl/arrowheads/arrowheads.glsl +12 -0
  127. vispy/glsl/arrowheads/arrowheads.vert +83 -0
  128. vispy/glsl/arrowheads/curved.glsl +48 -0
  129. vispy/glsl/arrowheads/inhibitor.glsl +26 -0
  130. vispy/glsl/arrowheads/stealth.glsl +46 -0
  131. vispy/glsl/arrowheads/triangle.glsl +97 -0
  132. vispy/glsl/arrowheads/util.glsl +13 -0
  133. vispy/glsl/arrows/angle-30.glsl +12 -0
  134. vispy/glsl/arrows/angle-60.glsl +12 -0
  135. vispy/glsl/arrows/angle-90.glsl +12 -0
  136. vispy/glsl/arrows/arrow.frag +39 -0
  137. vispy/glsl/arrows/arrow.vert +49 -0
  138. vispy/glsl/arrows/arrows.glsl +17 -0
  139. vispy/glsl/arrows/common.glsl +187 -0
  140. vispy/glsl/arrows/curved.glsl +63 -0
  141. vispy/glsl/arrows/stealth.glsl +50 -0
  142. vispy/glsl/arrows/triangle-30.glsl +12 -0
  143. vispy/glsl/arrows/triangle-60.glsl +12 -0
  144. vispy/glsl/arrows/triangle-90.glsl +12 -0
  145. vispy/glsl/arrows/util.glsl +98 -0
  146. vispy/glsl/build_spatial_filters.py +660 -0
  147. vispy/glsl/collections/agg-fast-path.frag +20 -0
  148. vispy/glsl/collections/agg-fast-path.vert +78 -0
  149. vispy/glsl/collections/agg-glyph.frag +60 -0
  150. vispy/glsl/collections/agg-glyph.vert +33 -0
  151. vispy/glsl/collections/agg-marker.frag +35 -0
  152. vispy/glsl/collections/agg-marker.vert +48 -0
  153. vispy/glsl/collections/agg-path.frag +55 -0
  154. vispy/glsl/collections/agg-path.vert +166 -0
  155. vispy/glsl/collections/agg-point.frag +21 -0
  156. vispy/glsl/collections/agg-point.vert +35 -0
  157. vispy/glsl/collections/agg-segment.frag +32 -0
  158. vispy/glsl/collections/agg-segment.vert +75 -0
  159. vispy/glsl/collections/marker.frag +38 -0
  160. vispy/glsl/collections/marker.vert +48 -0
  161. vispy/glsl/collections/raw-path.frag +15 -0
  162. vispy/glsl/collections/raw-path.vert +24 -0
  163. vispy/glsl/collections/raw-point.frag +14 -0
  164. vispy/glsl/collections/raw-point.vert +31 -0
  165. vispy/glsl/collections/raw-segment.frag +18 -0
  166. vispy/glsl/collections/raw-segment.vert +26 -0
  167. vispy/glsl/collections/raw-triangle.frag +13 -0
  168. vispy/glsl/collections/raw-triangle.vert +26 -0
  169. vispy/glsl/collections/sdf-glyph-ticks.vert +69 -0
  170. vispy/glsl/collections/sdf-glyph.frag +80 -0
  171. vispy/glsl/collections/sdf-glyph.vert +59 -0
  172. vispy/glsl/collections/tick-labels.vert +71 -0
  173. vispy/glsl/colormaps/autumn.glsl +20 -0
  174. vispy/glsl/colormaps/blues.glsl +20 -0
  175. vispy/glsl/colormaps/color-space.glsl +17 -0
  176. vispy/glsl/colormaps/colormaps.glsl +24 -0
  177. vispy/glsl/colormaps/cool.glsl +20 -0
  178. vispy/glsl/colormaps/fire.glsl +21 -0
  179. vispy/glsl/colormaps/gray.glsl +20 -0
  180. vispy/glsl/colormaps/greens.glsl +20 -0
  181. vispy/glsl/colormaps/hot.glsl +22 -0
  182. vispy/glsl/colormaps/ice.glsl +20 -0
  183. vispy/glsl/colormaps/icefire.glsl +23 -0
  184. vispy/glsl/colormaps/parse.py +40 -0
  185. vispy/glsl/colormaps/reds.glsl +20 -0
  186. vispy/glsl/colormaps/spring.glsl +20 -0
  187. vispy/glsl/colormaps/summer.glsl +20 -0
  188. vispy/glsl/colormaps/user.glsl +22 -0
  189. vispy/glsl/colormaps/util.glsl +41 -0
  190. vispy/glsl/colormaps/wheel.glsl +21 -0
  191. vispy/glsl/colormaps/winter.glsl +20 -0
  192. vispy/glsl/lines/agg.frag +320 -0
  193. vispy/glsl/lines/agg.vert +241 -0
  194. vispy/glsl/markers/arrow.glsl +12 -0
  195. vispy/glsl/markers/asterisk.glsl +16 -0
  196. vispy/glsl/markers/chevron.glsl +14 -0
  197. vispy/glsl/markers/clover.glsl +20 -0
  198. vispy/glsl/markers/club.glsl +31 -0
  199. vispy/glsl/markers/cross.glsl +17 -0
  200. vispy/glsl/markers/diamond.glsl +12 -0
  201. vispy/glsl/markers/disc.glsl +9 -0
  202. vispy/glsl/markers/ellipse.glsl +67 -0
  203. vispy/glsl/markers/hbar.glsl +9 -0
  204. vispy/glsl/markers/heart.glsl +15 -0
  205. vispy/glsl/markers/infinity.glsl +15 -0
  206. vispy/glsl/markers/marker-sdf.frag +74 -0
  207. vispy/glsl/markers/marker-sdf.vert +41 -0
  208. vispy/glsl/markers/marker.frag +36 -0
  209. vispy/glsl/markers/marker.vert +46 -0
  210. vispy/glsl/markers/markers.glsl +24 -0
  211. vispy/glsl/markers/pin.glsl +18 -0
  212. vispy/glsl/markers/ring.glsl +11 -0
  213. vispy/glsl/markers/spade.glsl +28 -0
  214. vispy/glsl/markers/square.glsl +10 -0
  215. vispy/glsl/markers/tag.glsl +11 -0
  216. vispy/glsl/markers/triangle.glsl +14 -0
  217. vispy/glsl/markers/vbar.glsl +9 -0
  218. vispy/glsl/math/circle-through-2-points.glsl +30 -0
  219. vispy/glsl/math/constants.glsl +48 -0
  220. vispy/glsl/math/double.glsl +114 -0
  221. vispy/glsl/math/functions.glsl +20 -0
  222. vispy/glsl/math/point-to-line-distance.glsl +31 -0
  223. vispy/glsl/math/point-to-line-projection.glsl +29 -0
  224. vispy/glsl/math/signed-line-distance.glsl +27 -0
  225. vispy/glsl/math/signed-segment-distance.glsl +30 -0
  226. vispy/glsl/misc/regular-grid.frag +244 -0
  227. vispy/glsl/misc/spatial-filters.frag +1407 -0
  228. vispy/glsl/misc/viewport-NDC.glsl +20 -0
  229. vispy/glsl/transforms/azimuthal-equal-area.glsl +32 -0
  230. vispy/glsl/transforms/azimuthal-equidistant.glsl +38 -0
  231. vispy/glsl/transforms/hammer.glsl +44 -0
  232. vispy/glsl/transforms/identity.glsl +6 -0
  233. vispy/glsl/transforms/identity_forward.glsl +23 -0
  234. vispy/glsl/transforms/identity_inverse.glsl +23 -0
  235. vispy/glsl/transforms/linear-scale.glsl +127 -0
  236. vispy/glsl/transforms/log-scale.glsl +126 -0
  237. vispy/glsl/transforms/mercator-transverse-forward.glsl +40 -0
  238. vispy/glsl/transforms/mercator-transverse-inverse.glsl +40 -0
  239. vispy/glsl/transforms/panzoom.glsl +10 -0
  240. vispy/glsl/transforms/polar.glsl +41 -0
  241. vispy/glsl/transforms/position.glsl +44 -0
  242. vispy/glsl/transforms/power-scale.glsl +139 -0
  243. vispy/glsl/transforms/projection.glsl +7 -0
  244. vispy/glsl/transforms/pvm.glsl +13 -0
  245. vispy/glsl/transforms/rotate.glsl +45 -0
  246. vispy/glsl/transforms/trackball.glsl +15 -0
  247. vispy/glsl/transforms/translate.glsl +35 -0
  248. vispy/glsl/transforms/transverse_mercator.glsl +38 -0
  249. vispy/glsl/transforms/viewport-clipping.glsl +14 -0
  250. vispy/glsl/transforms/viewport-transform.glsl +16 -0
  251. vispy/glsl/transforms/viewport.glsl +50 -0
  252. vispy/glsl/transforms/x.glsl +24 -0
  253. vispy/glsl/transforms/y.glsl +19 -0
  254. vispy/glsl/transforms/z.glsl +14 -0
  255. vispy/io/__init__.py +20 -0
  256. vispy/io/_data/spatial-filters.npy +0 -0
  257. vispy/io/datasets.py +94 -0
  258. vispy/io/image.py +231 -0
  259. vispy/io/mesh.py +122 -0
  260. vispy/io/stl.py +167 -0
  261. vispy/io/tests/__init__.py +0 -0
  262. vispy/io/tests/test_image.py +47 -0
  263. vispy/io/tests/test_io.py +121 -0
  264. vispy/io/wavefront.py +350 -0
  265. vispy/plot/__init__.py +36 -0
  266. vispy/plot/fig.py +58 -0
  267. vispy/plot/plotwidget.py +522 -0
  268. vispy/plot/tests/__init__.py +0 -0
  269. vispy/plot/tests/test_plot.py +46 -0
  270. vispy/scene/__init__.py +43 -0
  271. vispy/scene/cameras/__init__.py +27 -0
  272. vispy/scene/cameras/_base.py +38 -0
  273. vispy/scene/cameras/arcball.py +106 -0
  274. vispy/scene/cameras/base_camera.py +538 -0
  275. vispy/scene/cameras/fly.py +474 -0
  276. vispy/scene/cameras/magnify.py +163 -0
  277. vispy/scene/cameras/panzoom.py +308 -0
  278. vispy/scene/cameras/perspective.py +333 -0
  279. vispy/scene/cameras/tests/__init__.py +0 -0
  280. vispy/scene/cameras/tests/test_cameras.py +27 -0
  281. vispy/scene/cameras/tests/test_link.py +53 -0
  282. vispy/scene/cameras/tests/test_perspective.py +122 -0
  283. vispy/scene/cameras/turntable.py +173 -0
  284. vispy/scene/canvas.py +639 -0
  285. vispy/scene/events.py +85 -0
  286. vispy/scene/node.py +644 -0
  287. vispy/scene/subscene.py +20 -0
  288. vispy/scene/tests/__init__.py +0 -0
  289. vispy/scene/tests/test_canvas.py +119 -0
  290. vispy/scene/tests/test_node.py +142 -0
  291. vispy/scene/tests/test_visuals.py +141 -0
  292. vispy/scene/visuals.py +276 -0
  293. vispy/scene/widgets/__init__.py +18 -0
  294. vispy/scene/widgets/anchor.py +25 -0
  295. vispy/scene/widgets/axis.py +88 -0
  296. vispy/scene/widgets/colorbar.py +176 -0
  297. vispy/scene/widgets/console.py +351 -0
  298. vispy/scene/widgets/grid.py +509 -0
  299. vispy/scene/widgets/label.py +50 -0
  300. vispy/scene/widgets/tests/__init__.py +0 -0
  301. vispy/scene/widgets/tests/test_colorbar.py +47 -0
  302. vispy/scene/widgets/viewbox.py +199 -0
  303. vispy/scene/widgets/widget.py +478 -0
  304. vispy/testing/__init__.py +51 -0
  305. vispy/testing/_runners.py +446 -0
  306. vispy/testing/_testing.py +416 -0
  307. vispy/testing/image_tester.py +473 -0
  308. vispy/testing/rendered_array_tester.py +85 -0
  309. vispy/testing/tests/__init__.py +0 -0
  310. vispy/testing/tests/test_testing.py +20 -0
  311. vispy/util/__init__.py +17 -0
  312. vispy/util/bunch.py +15 -0
  313. vispy/util/check_environment.py +57 -0
  314. vispy/util/config.py +490 -0
  315. vispy/util/dpi/__init__.py +19 -0
  316. vispy/util/dpi/_linux.py +69 -0
  317. vispy/util/dpi/_quartz.py +26 -0
  318. vispy/util/dpi/_win32.py +34 -0
  319. vispy/util/dpi/tests/__init__.py +0 -0
  320. vispy/util/dpi/tests/test_dpi.py +16 -0
  321. vispy/util/eq.py +41 -0
  322. vispy/util/event.py +774 -0
  323. vispy/util/fetching.py +276 -0
  324. vispy/util/filter.py +44 -0
  325. vispy/util/fonts/__init__.py +14 -0
  326. vispy/util/fonts/_freetype.py +73 -0
  327. vispy/util/fonts/_quartz.py +192 -0
  328. vispy/util/fonts/_triage.py +36 -0
  329. vispy/util/fonts/_vispy_fonts.py +20 -0
  330. vispy/util/fonts/_win32.py +105 -0
  331. vispy/util/fonts/data/OpenSans-Bold.ttf +0 -0
  332. vispy/util/fonts/data/OpenSans-BoldItalic.ttf +0 -0
  333. vispy/util/fonts/data/OpenSans-Italic.ttf +0 -0
  334. vispy/util/fonts/data/OpenSans-Regular.ttf +0 -0
  335. vispy/util/fonts/tests/__init__.py +0 -0
  336. vispy/util/fonts/tests/test_font.py +45 -0
  337. vispy/util/fourier.py +69 -0
  338. vispy/util/frozen.py +25 -0
  339. vispy/util/gallery_scraper.py +268 -0
  340. vispy/util/keys.py +91 -0
  341. vispy/util/logs.py +358 -0
  342. vispy/util/osmesa_gl.py +17 -0
  343. vispy/util/profiler.py +135 -0
  344. vispy/util/ptime.py +16 -0
  345. vispy/util/quaternion.py +229 -0
  346. vispy/util/svg/__init__.py +18 -0
  347. vispy/util/svg/base.py +20 -0
  348. vispy/util/svg/color.py +219 -0
  349. vispy/util/svg/element.py +51 -0
  350. vispy/util/svg/geometry.py +478 -0
  351. vispy/util/svg/group.py +66 -0
  352. vispy/util/svg/length.py +81 -0
  353. vispy/util/svg/number.py +25 -0
  354. vispy/util/svg/path.py +332 -0
  355. vispy/util/svg/shapes.py +57 -0
  356. vispy/util/svg/style.py +59 -0
  357. vispy/util/svg/svg.py +40 -0
  358. vispy/util/svg/transform.py +223 -0
  359. vispy/util/svg/transformable.py +28 -0
  360. vispy/util/svg/viewport.py +73 -0
  361. vispy/util/tests/__init__.py +0 -0
  362. vispy/util/tests/test_config.py +58 -0
  363. vispy/util/tests/test_docstring_parameters.py +123 -0
  364. vispy/util/tests/test_emitter_group.py +262 -0
  365. vispy/util/tests/test_event_emitter.py +743 -0
  366. vispy/util/tests/test_fourier.py +35 -0
  367. vispy/util/tests/test_gallery_scraper.py +112 -0
  368. vispy/util/tests/test_import.py +127 -0
  369. vispy/util/tests/test_key.py +22 -0
  370. vispy/util/tests/test_logging.py +45 -0
  371. vispy/util/tests/test_run.py +14 -0
  372. vispy/util/tests/test_transforms.py +42 -0
  373. vispy/util/tests/test_vispy.py +48 -0
  374. vispy/util/transforms.py +201 -0
  375. vispy/util/wrappers.py +155 -0
  376. vispy/version.py +4 -0
  377. vispy/visuals/__init__.py +50 -0
  378. vispy/visuals/_scalable_textures.py +485 -0
  379. vispy/visuals/axis.py +678 -0
  380. vispy/visuals/border.py +208 -0
  381. vispy/visuals/box.py +79 -0
  382. vispy/visuals/collections/__init__.py +30 -0
  383. vispy/visuals/collections/agg_fast_path_collection.py +219 -0
  384. vispy/visuals/collections/agg_path_collection.py +197 -0
  385. vispy/visuals/collections/agg_point_collection.py +52 -0
  386. vispy/visuals/collections/agg_segment_collection.py +142 -0
  387. vispy/visuals/collections/array_list.py +401 -0
  388. vispy/visuals/collections/base_collection.py +482 -0
  389. vispy/visuals/collections/collection.py +253 -0
  390. vispy/visuals/collections/path_collection.py +23 -0
  391. vispy/visuals/collections/point_collection.py +19 -0
  392. vispy/visuals/collections/polygon_collection.py +25 -0
  393. vispy/visuals/collections/raw_path_collection.py +119 -0
  394. vispy/visuals/collections/raw_point_collection.py +113 -0
  395. vispy/visuals/collections/raw_polygon_collection.py +77 -0
  396. vispy/visuals/collections/raw_segment_collection.py +112 -0
  397. vispy/visuals/collections/raw_triangle_collection.py +78 -0
  398. vispy/visuals/collections/segment_collection.py +19 -0
  399. vispy/visuals/collections/triangle_collection.py +16 -0
  400. vispy/visuals/collections/util.py +168 -0
  401. vispy/visuals/colorbar.py +699 -0
  402. vispy/visuals/cube.py +41 -0
  403. vispy/visuals/ellipse.py +163 -0
  404. vispy/visuals/filters/__init__.py +10 -0
  405. vispy/visuals/filters/base_filter.py +242 -0
  406. vispy/visuals/filters/clipper.py +60 -0
  407. vispy/visuals/filters/clipping_planes.py +122 -0
  408. vispy/visuals/filters/color.py +181 -0
  409. vispy/visuals/filters/markers.py +28 -0
  410. vispy/visuals/filters/mesh.py +796 -0
  411. vispy/visuals/filters/picking.py +60 -0
  412. vispy/visuals/filters/tests/__init__.py +3 -0
  413. vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
  414. vispy/visuals/filters/tests/test_wireframe_filter.py +16 -0
  415. vispy/visuals/glsl/__init__.py +1 -0
  416. vispy/visuals/glsl/antialiasing.py +133 -0
  417. vispy/visuals/glsl/color.py +63 -0
  418. vispy/visuals/graphs/__init__.py +1 -0
  419. vispy/visuals/graphs/graph.py +240 -0
  420. vispy/visuals/graphs/layouts/__init__.py +55 -0
  421. vispy/visuals/graphs/layouts/circular.py +49 -0
  422. vispy/visuals/graphs/layouts/force_directed.py +211 -0
  423. vispy/visuals/graphs/layouts/networkx_layout.py +87 -0
  424. vispy/visuals/graphs/layouts/random.py +52 -0
  425. vispy/visuals/graphs/tests/__init__.py +1 -0
  426. vispy/visuals/graphs/tests/test_layouts.py +139 -0
  427. vispy/visuals/graphs/tests/test_networkx_layout.py +47 -0
  428. vispy/visuals/graphs/util.py +120 -0
  429. vispy/visuals/gridlines.py +105 -0
  430. vispy/visuals/gridmesh.py +98 -0
  431. vispy/visuals/histogram.py +58 -0
  432. vispy/visuals/image.py +688 -0
  433. vispy/visuals/image_complex.py +130 -0
  434. vispy/visuals/infinite_line.py +199 -0
  435. vispy/visuals/instanced_mesh.py +152 -0
  436. vispy/visuals/isocurve.py +213 -0
  437. vispy/visuals/isoline.py +241 -0
  438. vispy/visuals/isosurface.py +113 -0
  439. vispy/visuals/line/__init__.py +6 -0
  440. vispy/visuals/line/arrow.py +289 -0
  441. vispy/visuals/line/dash_atlas.py +90 -0
  442. vispy/visuals/line/line.py +545 -0
  443. vispy/visuals/line_plot.py +135 -0
  444. vispy/visuals/linear_region.py +199 -0
  445. vispy/visuals/markers.py +810 -0
  446. vispy/visuals/mesh.py +373 -0
  447. vispy/visuals/mesh_normals.py +159 -0
  448. vispy/visuals/plane.py +54 -0
  449. vispy/visuals/polygon.py +145 -0
  450. vispy/visuals/rectangle.py +196 -0
  451. vispy/visuals/regular_polygon.py +56 -0
  452. vispy/visuals/scrolling_lines.py +197 -0
  453. vispy/visuals/shaders/__init__.py +17 -0
  454. vispy/visuals/shaders/compiler.py +206 -0
  455. vispy/visuals/shaders/expression.py +99 -0
  456. vispy/visuals/shaders/function.py +788 -0
  457. vispy/visuals/shaders/multiprogram.py +145 -0
  458. vispy/visuals/shaders/parsing.py +140 -0
  459. vispy/visuals/shaders/program.py +161 -0
  460. vispy/visuals/shaders/shader_object.py +162 -0
  461. vispy/visuals/shaders/tests/__init__.py +0 -0
  462. vispy/visuals/shaders/tests/test_function.py +486 -0
  463. vispy/visuals/shaders/tests/test_multiprogram.py +78 -0
  464. vispy/visuals/shaders/tests/test_parsing.py +57 -0
  465. vispy/visuals/shaders/variable.py +272 -0
  466. vispy/visuals/spectrogram.py +169 -0
  467. vispy/visuals/sphere.py +80 -0
  468. vispy/visuals/surface_plot.py +192 -0
  469. vispy/visuals/tests/__init__.py +0 -0
  470. vispy/visuals/tests/test_arrows.py +109 -0
  471. vispy/visuals/tests/test_axis.py +120 -0
  472. vispy/visuals/tests/test_collections.py +15 -0
  473. vispy/visuals/tests/test_colorbar.py +179 -0
  474. vispy/visuals/tests/test_colormap.py +97 -0
  475. vispy/visuals/tests/test_ellipse.py +122 -0
  476. vispy/visuals/tests/test_histogram.py +24 -0
  477. vispy/visuals/tests/test_image.py +390 -0
  478. vispy/visuals/tests/test_image_complex.py +36 -0
  479. vispy/visuals/tests/test_infinite_line.py +53 -0
  480. vispy/visuals/tests/test_instanced_mesh.py +50 -0
  481. vispy/visuals/tests/test_isosurface.py +22 -0
  482. vispy/visuals/tests/test_linear_region.py +152 -0
  483. vispy/visuals/tests/test_markers.py +54 -0
  484. vispy/visuals/tests/test_mesh.py +261 -0
  485. vispy/visuals/tests/test_mesh_normals.py +218 -0
  486. vispy/visuals/tests/test_polygon.py +112 -0
  487. vispy/visuals/tests/test_rectangle.py +163 -0
  488. vispy/visuals/tests/test_regular_polygon.py +111 -0
  489. vispy/visuals/tests/test_scalable_textures.py +180 -0
  490. vispy/visuals/tests/test_sdf.py +73 -0
  491. vispy/visuals/tests/test_spectrogram.py +42 -0
  492. vispy/visuals/tests/test_text.py +95 -0
  493. vispy/visuals/tests/test_volume.py +542 -0
  494. vispy/visuals/tests/test_windbarb.py +33 -0
  495. vispy/visuals/text/__init__.py +7 -0
  496. vispy/visuals/text/_sdf_cpu.cpython-312-aarch64-linux-gnu.so +0 -0
  497. vispy/visuals/text/_sdf_cpu.pyx +110 -0
  498. vispy/visuals/text/_sdf_gpu.py +316 -0
  499. vispy/visuals/text/text.py +675 -0
  500. vispy/visuals/transforms/__init__.py +34 -0
  501. vispy/visuals/transforms/_util.py +191 -0
  502. vispy/visuals/transforms/base_transform.py +233 -0
  503. vispy/visuals/transforms/chain.py +300 -0
  504. vispy/visuals/transforms/interactive.py +98 -0
  505. vispy/visuals/transforms/linear.py +564 -0
  506. vispy/visuals/transforms/nonlinear.py +398 -0
  507. vispy/visuals/transforms/tests/__init__.py +0 -0
  508. vispy/visuals/transforms/tests/test_transforms.py +243 -0
  509. vispy/visuals/transforms/transform_system.py +339 -0
  510. vispy/visuals/tube.py +173 -0
  511. vispy/visuals/visual.py +923 -0
  512. vispy/visuals/volume.py +1335 -0
  513. vispy/visuals/windbarb.py +291 -0
  514. vispy/visuals/xyz_axis.py +34 -0
  515. vispy-0.14.0.dist-info/LICENSE.txt +36 -0
  516. vispy-0.14.0.dist-info/METADATA +218 -0
  517. vispy-0.14.0.dist-info/RECORD +519 -0
  518. vispy-0.14.0.dist-info/WHEEL +6 -0
  519. vispy-0.14.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,876 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import division, print_function
4
+
5
+ from itertools import permutations
6
+ import numpy as np
7
+
8
+ from collections import OrderedDict
9
+
10
+
11
+ class Triangulation(object):
12
+ """Constrained delaunay triangulation
13
+
14
+ Implementation based on [1]_.
15
+
16
+ Parameters
17
+ ----------
18
+ pts : array
19
+ Nx2 array of points.
20
+ edges : array
21
+ Nx2 array of edges (dtype=int).
22
+
23
+ Notes
24
+ -----
25
+ * Delaunay legalization is not yet implemented. This produces a proper
26
+ triangulation, but adding legalisation would produce fewer thin
27
+ triangles.
28
+ * The pts and edges arrays may be modified.
29
+
30
+ References
31
+ ----------
32
+ .. [1] Domiter, V. and Žalik, B. Sweep‐line algorithm for constrained
33
+ Delaunay triangulation
34
+ """
35
+
36
+ def __init__(self, pts, edges):
37
+ self.pts = pts[:, :2].astype(np.float32)
38
+ self.edges = edges
39
+ if self.pts.ndim != 2 or self.pts.shape[1] != 2:
40
+ raise TypeError('pts argument must be ndarray of shape (N, 2).')
41
+ if self.edges.ndim != 2 or self.edges.shape[1] != 2:
42
+ raise TypeError('edges argument must be ndarray of shape (N, 2).')
43
+
44
+ # described in initialize()
45
+ self._front = None
46
+ self.tris = OrderedDict()
47
+ self._edges_lookup = {}
48
+
49
+ def _normalize(self):
50
+ # Clean up data (not discussed in original publication)
51
+
52
+ # (i) Split intersecting edges. Every edge that intersects another
53
+ # edge or point is split. This extends self.pts and self.edges.
54
+ self._split_intersecting_edges()
55
+
56
+ # (ii) Merge identical points. If any two points are found to be equal,
57
+ # the second is removed and the edge table is updated accordingly.
58
+ self._merge_duplicate_points()
59
+
60
+ # (iii) Remove duplicate edges
61
+ # TODO
62
+
63
+ def _initialize(self):
64
+ self._normalize()
65
+ # Initialization (sec. 3.3)
66
+
67
+ # sort points by y, then x
68
+ flat_shape = self.pts.shape[0] * self.pts.shape[1]
69
+ pts = self.pts.reshape(flat_shape).view([('x', np.float32),
70
+ ('y', np.float32)])
71
+ order = np.argsort(pts, order=('y', 'x'))
72
+ pts = pts[order]
73
+ # update edges to match new point order
74
+ invorder = np.argsort(order)
75
+ self.edges = invorder[self.edges]
76
+ self.pts = pts.view(np.float32).reshape(len(pts), 2)
77
+
78
+ # make artificial points P-1 and P-2
79
+ xmax = self.pts[:, 0].max()
80
+ xmin = self.pts[:, 0].min()
81
+ ymax = self.pts[:, 1].max()
82
+ ymin = self.pts[:, 1].min()
83
+ xa = (xmax-xmin) * 0.3
84
+ ya = (ymax-ymin) * 0.3
85
+ p1 = (xmin - xa, ymin - ya)
86
+ p2 = (xmax + xa, ymin - ya)
87
+
88
+ # prepend artificial points to point list
89
+ newpts = np.empty((self.pts.shape[0]+2, 2), dtype=float)
90
+ newpts[0] = p1
91
+ newpts[1] = p2
92
+ newpts[2:] = self.pts
93
+ self.pts = newpts
94
+ self.edges += 2
95
+
96
+ # find topmost point in each edge
97
+ self._tops = self.edges.max(axis=1)
98
+ self._bottoms = self.edges.min(axis=1)
99
+
100
+ # inintialize sweep front
101
+ # values in this list are indexes into self.pts
102
+ self._front = [0, 2, 1]
103
+
104
+ # empty triangle list.
105
+ # This will contain [(a, b, c), ...] where a,b,c are indexes into
106
+ # self.pts
107
+ self.tris = OrderedDict()
108
+
109
+ # For each triangle, maps (a, b): c
110
+ # This is used to look up the thrid point in a triangle, given any
111
+ # edge. Since each edge has two triangles, they are independently
112
+ # stored as (a, b): c and (b, a): d
113
+ self._edges_lookup = {}
114
+
115
+ def triangulate(self):
116
+ """Do the triangulation."""
117
+ self._initialize()
118
+
119
+ pts = self.pts
120
+ front = self._front
121
+
122
+ # Begin sweep (sec. 3.4)
123
+ for i in range(3, pts.shape[0]):
124
+ pi = pts[i]
125
+
126
+ # First, triangulate from front to new point
127
+ # This applies to both "point events" (3.4.1)
128
+ # and "edge events" (3.4.2).
129
+
130
+ # get index along front that intersects pts[i]
131
+ idx = 0
132
+ while pts[front[idx+1], 0] <= pi[0]:
133
+ idx += 1
134
+ pl = pts[front[idx]]
135
+
136
+ # "(i) middle case"
137
+ if pi[0] > pl[0]:
138
+ # Add a single triangle connecting pi,pl,pr
139
+ self._add_tri(front[idx], front[idx+1], i)
140
+ front.insert(idx+1, i)
141
+ # "(ii) left case"
142
+ else:
143
+ # Add triangles connecting pi,pl,ps and pi,pl,pr
144
+ self._add_tri(front[idx], front[idx+1], i)
145
+ self._add_tri(front[idx-1], front[idx], i)
146
+ front[idx] = i
147
+
148
+ # Continue adding triangles to smooth out front
149
+ # (heuristics shown in figs. 9, 10)
150
+ for direction in -1, 1:
151
+ while True:
152
+ # Find point connected to pi
153
+ ind0 = front.index(i)
154
+ ind1 = ind0 + direction
155
+ ind2 = ind1 + direction
156
+ if ind2 < 0 or ind2 >= len(front):
157
+ break
158
+
159
+ # measure angle made with front
160
+ p1 = pts[front[ind1]]
161
+ p2 = pts[front[ind2]]
162
+ err = np.geterr()
163
+ np.seterr(invalid='ignore')
164
+ try:
165
+ angle = np.arccos(self._cosine(pi, p1, p2))
166
+ finally:
167
+ np.seterr(**err)
168
+
169
+ # if angle is < pi/2, make new triangle
170
+ if angle > np.pi/2. or np.isnan(angle):
171
+ break
172
+
173
+ assert (i != front[ind1] and
174
+ front[ind1] != front[ind2] and
175
+ front[ind2] != i)
176
+ self._add_tri(i, front[ind1], front[ind2])
177
+ front.pop(ind1)
178
+
179
+ # "edge event" (sec. 3.4.2)
180
+ # remove any triangles cut by completed edges and re-fill
181
+ # the holes.
182
+ if i in self._tops:
183
+ for j in self._bottoms[self._tops == i]:
184
+ # Make sure edge (j, i) is present in mesh
185
+ # because edge event may have created a new front list
186
+ self._edge_event(i, j)
187
+ front = self._front
188
+
189
+ self._finalize()
190
+
191
+ self.tris = np.array(list(self.tris.keys()), dtype=int)
192
+
193
+ def _finalize(self):
194
+ # Finalize (sec. 3.5)
195
+
196
+ # (i) Add bordering triangles to fill hull
197
+ front = list(OrderedDict.fromkeys(self._front))
198
+
199
+ idx = len(front) - 2
200
+ k = 1
201
+ while k < idx-1:
202
+ # if edges lie in counterclockwise direction, then signed area
203
+ # is positive
204
+ if self._iscounterclockwise(front[k], front[k+1], front[k+2]):
205
+ self._add_tri(front[k], front[k+1], front[k+2])
206
+ front.pop(k+1)
207
+ idx -= 1
208
+ continue
209
+ k += 1
210
+
211
+ # (ii) Remove all triangles not inside the hull
212
+ # (not described in article)
213
+
214
+ tris = [] # triangles to check
215
+ tri_state = {} # 0 for outside, 1 for inside
216
+
217
+ # find a starting triangle
218
+ for t in self.tris:
219
+ if 0 in t or 1 in t:
220
+ tri_state[t] = 0
221
+ tris.append(t)
222
+ break
223
+
224
+ while tris:
225
+ next_tris = []
226
+ for t in tris:
227
+ v = tri_state[t]
228
+ for i in (0, 1, 2):
229
+ edge = (t[i], t[(i + 1) % 3])
230
+ pt = t[(i + 2) % 3]
231
+ t2 = self._adjacent_tri(edge, pt)
232
+ if t2 is None:
233
+ continue
234
+ t2a = t2[1:3] + t2[0:1]
235
+ t2b = t2[2:3] + t2[0:2]
236
+ if t2 in tri_state or t2a in tri_state or t2b in tri_state:
237
+ continue
238
+ if self._is_constraining_edge(edge):
239
+ tri_state[t2] = 1 - v
240
+ else:
241
+ tri_state[t2] = v
242
+ next_tris.append(t2)
243
+ tris = next_tris
244
+
245
+ for t, v in tri_state.items():
246
+ if v == 0:
247
+ self._remove_tri(*t)
248
+
249
+ def _edge_event(self, i, j):
250
+ """Force edge (i, j) to be present in mesh.
251
+
252
+ This works by removing intersected triangles and filling holes up to
253
+ the cutting edge.
254
+ """
255
+ front_index = self._front.index(i)
256
+
257
+ front = self._front
258
+
259
+ # First just see whether this edge is already present
260
+ # (this is not in the published algorithm)
261
+ if (i, j) in self._edges_lookup or (j, i) in self._edges_lookup:
262
+ return
263
+
264
+ # traverse in two different modes:
265
+ # 1. If cutting edge is below front, traverse through triangles. These
266
+ # must be removed and the resulting hole re-filled. (fig. 12)
267
+ # 2. If cutting edge is above the front, then follow the front until
268
+ # crossing under again. (fig. 13)
269
+ # We must be able to switch back and forth between these
270
+ # modes (fig. 14)
271
+
272
+ # Collect points that draw the open polygons on either side of the
273
+ # cutting edge. Note that our use of 'upper' and 'lower' is not strict;
274
+ # in some cases the two may be swapped.
275
+ upper_polygon = [i]
276
+ lower_polygon = [i]
277
+
278
+ # Keep track of which section of the front must be replaced
279
+ # and with what it should be replaced
280
+ front_holes = [] # contains indexes for sections of front to remove
281
+
282
+ next_tri = None # next triangle to cut (already set if in mode 1)
283
+ last_edge = None # or last triangle edge crossed (if in mode 1)
284
+
285
+ # Which direction to traverse front
286
+ front_dir = 1 if self.pts[j][0] > self.pts[i][0] else -1
287
+
288
+ # Initialize search state
289
+ if self._edge_below_front((i, j), front_index):
290
+ mode = 1 # follow triangles
291
+ tri = self._find_cut_triangle((i, j))
292
+ last_edge = self._edge_opposite_point(tri, i)
293
+ next_tri = self._adjacent_tri(last_edge, i)
294
+ assert next_tri is not None
295
+ self._remove_tri(*tri)
296
+ # todo: does this work? can we count on last_edge to be clockwise
297
+ # around point i?
298
+ lower_polygon.append(last_edge[1])
299
+ upper_polygon.append(last_edge[0])
300
+ else:
301
+ mode = 2 # follow front
302
+
303
+ # Loop until we reach point j
304
+ while True:
305
+ if mode == 1:
306
+ # crossing from one triangle into another
307
+ if j in next_tri:
308
+ # reached endpoint!
309
+ # update front / polygons
310
+ upper_polygon.append(j)
311
+ lower_polygon.append(j)
312
+ self._remove_tri(*next_tri)
313
+ break
314
+ else:
315
+ # next triangle does not contain the end point; we will
316
+ # cut one of the two far edges.
317
+ tri_edges = self._edges_in_tri_except(next_tri, last_edge)
318
+
319
+ # select the edge that is cut
320
+ last_edge = self._intersected_edge(tri_edges, (i, j))
321
+ last_tri = next_tri
322
+ next_tri = self._adjacent_tri(last_edge, last_tri)
323
+ self._remove_tri(*last_tri)
324
+
325
+ # Crossing an edge adds one point to one of the polygons
326
+ if lower_polygon[-1] == last_edge[0]:
327
+ upper_polygon.append(last_edge[1])
328
+ elif lower_polygon[-1] == last_edge[1]:
329
+ upper_polygon.append(last_edge[0])
330
+ elif upper_polygon[-1] == last_edge[0]:
331
+ lower_polygon.append(last_edge[1])
332
+ elif upper_polygon[-1] == last_edge[1]:
333
+ lower_polygon.append(last_edge[0])
334
+ else:
335
+ raise RuntimeError("Something went wrong..")
336
+
337
+ # If we crossed the front, go to mode 2
338
+ x = self._edge_in_front(last_edge)
339
+ if x >= 0: # crossing over front
340
+ mode = 2
341
+ next_tri = None
342
+
343
+ # where did we cross the front?
344
+ # nearest to new point
345
+ front_index = x + (1 if front_dir == -1 else 0)
346
+
347
+ # Select the correct polygon to be lower_polygon
348
+ # (because mode 2 requires this).
349
+ # We know that last_edge is in the front, and
350
+ # front[front_index] is the point _above_ the front.
351
+ # So if this point is currently the last element in
352
+ # lower_polygon, then the polys must be swapped.
353
+ if lower_polygon[-1] == front[front_index]:
354
+ tmp = lower_polygon, upper_polygon
355
+ upper_polygon, lower_polygon = tmp
356
+ else:
357
+ assert upper_polygon[-1] == front[front_index]
358
+
359
+ else:
360
+ assert next_tri is not None
361
+
362
+ else: # mode == 2
363
+ # At each iteration, we require:
364
+ # * front_index is the starting index of the edge _preceding_
365
+ # the edge that will be handled in this iteration
366
+ # * lower_polygon is the polygon to which points should be
367
+ # added while traversing the front
368
+
369
+ front_index += front_dir
370
+ next_edge = (front[front_index], front[front_index+front_dir])
371
+
372
+ assert front_index >= 0
373
+ if front[front_index] == j:
374
+ # found endpoint!
375
+ lower_polygon.append(j)
376
+ upper_polygon.append(j)
377
+ break
378
+
379
+ # Add point to lower_polygon.
380
+ # The conditional is because there are cases where the
381
+ # point was already added if we just crossed from mode 1.
382
+ if lower_polygon[-1] != front[front_index]:
383
+ lower_polygon.append(front[front_index])
384
+
385
+ front_holes.append(front_index)
386
+
387
+ if self._edges_intersect((i, j), next_edge):
388
+ # crossing over front into triangle
389
+ mode = 1
390
+
391
+ last_edge = next_edge
392
+
393
+ # we are crossing the front, so this edge only has one
394
+ # triangle.
395
+ next_tri = self._tri_from_edge(last_edge)
396
+
397
+ upper_polygon.append(front[front_index+front_dir])
398
+
399
+ # (iii) triangluate empty areas
400
+
401
+ for polygon in [lower_polygon, upper_polygon]:
402
+ dist = self._distances_from_line((i, j), polygon)
403
+ while len(polygon) > 2:
404
+ ind = np.argmax(dist)
405
+ self._add_tri(polygon[ind], polygon[ind-1],
406
+ polygon[ind+1])
407
+ polygon.pop(ind)
408
+ dist.pop(ind)
409
+
410
+ # update front by removing points in the holes (places where front
411
+ # passes below the cut edge)
412
+ front_holes.sort(reverse=True)
413
+ for i in front_holes:
414
+ front.pop(i)
415
+
416
+ def _find_cut_triangle(self, edge):
417
+ """
418
+ Return the triangle that has edge[0] as one of its vertices and is
419
+ bisected by edge.
420
+
421
+ Return None if no triangle is found.
422
+ """
423
+ edges = [] # opposite edge for each triangle attached to edge[0]
424
+ for tri in self.tris:
425
+ if edge[0] in tri:
426
+ edges.append(self._edge_opposite_point(tri, edge[0]))
427
+
428
+ for oedge in edges:
429
+ o1 = self._orientation(edge, oedge[0])
430
+ o2 = self._orientation(edge, oedge[1])
431
+ if o1 != o2:
432
+ return (edge[0], oedge[0], oedge[1])
433
+
434
+ return None
435
+
436
+ def _edge_in_front(self, edge):
437
+ """Return the index where *edge* appears in the current front.
438
+
439
+ If the edge is not in the front, return -1
440
+ """
441
+ e = (list(edge), list(edge)[::-1])
442
+ for i in range(len(self._front)-1):
443
+ if self._front[i:i+2] in e:
444
+ return i
445
+ return -1
446
+
447
+ def _edge_opposite_point(self, tri, i):
448
+ """Given a triangle, return the edge that is opposite point i.
449
+
450
+ Vertexes are returned in the same orientation as in tri.
451
+ """
452
+ ind = tri.index(i)
453
+ return (tri[(ind+1) % 3], tri[(ind+2) % 3])
454
+
455
+ def _adjacent_tri(self, edge, i):
456
+ """Given a triangle formed by edge and i, return the triangle that shares
457
+ edge. *i* may be either a point or the entire triangle.
458
+ """
459
+ if not np.isscalar(i):
460
+ i = [x for x in i if x not in edge][0]
461
+
462
+ try:
463
+ pt1 = self._edges_lookup[edge]
464
+ pt2 = self._edges_lookup[(edge[1], edge[0])]
465
+ except KeyError:
466
+ return None
467
+
468
+ if pt1 == i:
469
+ return (edge[1], edge[0], pt2)
470
+ elif pt2 == i:
471
+ return (edge[1], edge[0], pt1)
472
+ else:
473
+ raise RuntimeError("Edge %s and point %d do not form a triangle "
474
+ "in this mesh." % (edge, i))
475
+
476
+ def _tri_from_edge(self, edge):
477
+ """Return the only tri that contains *edge*.
478
+
479
+ If two tris share this edge, raise an exception.
480
+ """
481
+ edge = tuple(edge)
482
+ p1 = self._edges_lookup.get(edge, None)
483
+ p2 = self._edges_lookup.get(edge[::-1], None)
484
+ if p1 is None:
485
+ if p2 is None:
486
+ raise RuntimeError("No tris connected to edge %r" % (edge,))
487
+ return edge + (p2,)
488
+ elif p2 is None:
489
+ return edge + (p1,)
490
+ else:
491
+ raise RuntimeError("Two triangles connected to edge %r" % (edge,))
492
+
493
+ def _edges_in_tri_except(self, tri, edge):
494
+ """Return the edges in *tri*, excluding *edge*."""
495
+ edges = [(tri[i], tri[(i+1) % 3]) for i in range(3)]
496
+ try:
497
+ edges.remove(tuple(edge))
498
+ except ValueError:
499
+ edges.remove(tuple(edge[::-1]))
500
+ return edges
501
+
502
+ def _edge_below_front(self, edge, front_index):
503
+ """Return True if *edge* is below the current front.
504
+
505
+ One of the points in *edge* must be _on_ the front, at *front_index*.
506
+ """
507
+ f0 = self._front[front_index-1]
508
+ f1 = self._front[front_index+1]
509
+ return (self._orientation(edge, f0) > 0 and
510
+ self._orientation(edge, f1) < 0)
511
+
512
+ def _is_constraining_edge(self, edge):
513
+ mask1 = self.edges == edge[0]
514
+ mask2 = self.edges == edge[1]
515
+ return (np.any(mask1[:, 0] & mask2[:, 1]) or
516
+ np.any(mask2[:, 0] & mask1[:, 1]))
517
+
518
+ def _intersected_edge(self, edges, cut_edge):
519
+ """Given a list of *edges*, return the first that is intersected by
520
+ *cut_edge*.
521
+ """
522
+ for edge in edges:
523
+ if self._edges_intersect(edge, cut_edge):
524
+ return edge
525
+
526
+ def _find_edge_intersections(self):
527
+ """Return a dictionary containing, for each edge in self.edges, a list
528
+ of the positions at which the edge should be split.
529
+ """
530
+ edges = self.pts[self.edges]
531
+ cuts = {} # { edge: [(intercept, point), ...], ... }
532
+ for i in range(edges.shape[0]-1):
533
+ # intersection of edge i onto all others
534
+ int1 = self._intersect_edge_arrays(edges[i:i+1], edges[i+1:])
535
+ # intersection of all edges onto edge i
536
+ int2 = self._intersect_edge_arrays(edges[i+1:], edges[i:i+1])
537
+
538
+ # select for pairs that intersect
539
+ err = np.geterr()
540
+ np.seterr(divide='ignore', invalid='ignore')
541
+ try:
542
+ mask1 = (int1 >= 0) & (int1 <= 1)
543
+ mask2 = (int2 >= 0) & (int2 <= 1)
544
+ mask3 = mask1 & mask2 # all intersections
545
+ finally:
546
+ np.seterr(**err)
547
+
548
+ # compute points of intersection
549
+ inds = np.argwhere(mask3)[:, 0]
550
+ if len(inds) == 0:
551
+ continue
552
+ h = int2[inds][:, np.newaxis]
553
+ pts = (edges[i, 0][np.newaxis, :] * (1.0 - h) +
554
+ edges[i, 1][np.newaxis, :] * h)
555
+
556
+ # record for all edges the location of cut points
557
+ edge_cuts = cuts.setdefault(i, [])
558
+ for j, ind in enumerate(inds):
559
+ if 0 < int2[ind] < 1:
560
+ edge_cuts.append((int2[ind], pts[j]))
561
+ if 0 < int1[ind] < 1:
562
+ other_cuts = cuts.setdefault(ind+i+1, [])
563
+ other_cuts.append((int1[ind], pts[j]))
564
+
565
+ # sort all cut lists by intercept, remove duplicates
566
+ for k, v in cuts.items():
567
+ v.sort(key=lambda x: x[0])
568
+ for i in range(len(v)-2, -1, -1):
569
+ if v[i][0] == v[i+1][0]:
570
+ v.pop(i+1)
571
+ return cuts
572
+
573
+ def _split_intersecting_edges(self):
574
+ # we can do all intersections at once, but this has excessive memory
575
+ # overhead.
576
+
577
+ # measure intersection point between all pairs of edges
578
+ all_cuts = self._find_edge_intersections()
579
+
580
+ # cut edges at each intersection
581
+ add_pts = []
582
+ add_edges = []
583
+ for edge, cuts in all_cuts.items():
584
+ if len(cuts) == 0:
585
+ continue
586
+
587
+ # add new points
588
+ pt_offset = self.pts.shape[0] + len(add_pts)
589
+ new_pts = [x[1] for x in cuts]
590
+ add_pts.extend(new_pts)
591
+
592
+ # list of point indexes for all new edges
593
+ pt_indexes = list(range(pt_offset, pt_offset + len(cuts)))
594
+ pt_indexes.append(self.edges[edge, 1])
595
+
596
+ # modify original edge
597
+ self.edges[edge, 1] = pt_indexes[0]
598
+
599
+ # add new edges
600
+ new_edges = [[pt_indexes[i-1], pt_indexes[i]]
601
+ for i in range(1, len(pt_indexes))]
602
+ add_edges.extend(new_edges)
603
+
604
+ if add_pts:
605
+ add_pts = np.array(add_pts, dtype=self.pts.dtype)
606
+ self.pts = np.append(self.pts, add_pts, axis=0)
607
+ if add_edges:
608
+ add_edges = np.array(add_edges, dtype=self.edges.dtype)
609
+ self.edges = np.append(self.edges, add_edges, axis=0)
610
+
611
+ def _merge_duplicate_points(self):
612
+ # generate a list of all pairs (i,j) of identical points
613
+ dups = []
614
+ for i in range(self.pts.shape[0]-1):
615
+ test_pt = self.pts[i:i+1]
616
+ comp_pts = self.pts[i+1:]
617
+ eq = test_pt == comp_pts
618
+ eq = eq[:, 0] & eq[:, 1]
619
+ for j in np.argwhere(eq)[:, 0]:
620
+ dups.append((i, i+1+j))
621
+
622
+ dups_arr = np.array(dups)
623
+ # remove duplicate points
624
+ pt_mask = np.ones(self.pts.shape[0], dtype=bool)
625
+ for i, inds in enumerate(dups_arr):
626
+ # remove j from points
627
+ # (note we pull the index from the original dups instead of
628
+ # dups_arr because the indexes in pt_mask do not change)
629
+ pt_mask[dups[i][1]] = False
630
+
631
+ i, j = inds
632
+
633
+ # rewrite edges to use i instead of j
634
+ self.edges[self.edges == j] = i
635
+
636
+ # decrement all point indexes > j
637
+ self.edges[self.edges > j] -= 1
638
+ dups_arr[dups_arr > j] -= 1
639
+
640
+ self.pts = self.pts[pt_mask]
641
+
642
+ # remove zero-length edges
643
+ mask = self.edges[:, 0] != self.edges[:, 1]
644
+ self.edges = self.edges[mask]
645
+
646
+ def _distances_from_line(self, edge, points):
647
+ # Distance of a set of points from a given line
648
+ e1 = self.pts[edge[0]]
649
+ e2 = self.pts[edge[1]]
650
+ distances = []
651
+ for i in points:
652
+ p = self.pts[i]
653
+ proj = self._projection(e1, p, e2)
654
+ distances.append(((p - proj)**2).sum()**0.5)
655
+ assert distances[0] == 0 and distances[-1] == 0
656
+ return distances
657
+
658
+ def _projection(self, a, b, c):
659
+ """Return projection of (a,b) onto (a,c)
660
+ Arguments are point locations, not indexes.
661
+ """
662
+ ab = b - a
663
+ ac = c - a
664
+ return a + ((ab*ac).sum() / (ac*ac).sum()) * ac
665
+
666
+ def _cosine(self, A, B, C):
667
+ # Cosine of angle ABC
668
+ a = ((C - B)**2).sum()
669
+ b = ((C - A)**2).sum()
670
+ c = ((B - A)**2).sum()
671
+ d = (a + c - b) / ((4 * a * c)**0.5)
672
+ return d
673
+
674
+ def _iscounterclockwise(self, a, b, c):
675
+ # Check if the points lie in counter-clockwise order or not
676
+ A = self.pts[a]
677
+ B = self.pts[b]
678
+ C = self.pts[c]
679
+ return np.cross(B-A, C-B) > 0
680
+
681
+ def _edges_intersect(self, edge1, edge2):
682
+ """Return 1 if edges intersect completely (endpoints excluded)"""
683
+ h12 = self._intersect_edge_arrays(self.pts[np.array(edge1)],
684
+ self.pts[np.array(edge2)])
685
+ h21 = self._intersect_edge_arrays(self.pts[np.array(edge2)],
686
+ self.pts[np.array(edge1)])
687
+ err = np.geterr()
688
+ np.seterr(divide='ignore', invalid='ignore')
689
+ try:
690
+ out = (0 < h12 < 1) and (0 < h21 < 1)
691
+ finally:
692
+ np.seterr(**err)
693
+ return out
694
+
695
+ def _intersect_edge_arrays(self, lines1, lines2):
696
+ """Return the intercepts of all lines defined in *lines1* as they
697
+ intersect all lines in *lines2*.
698
+
699
+ Arguments are of shape (..., 2, 2), where axes are:
700
+
701
+ 0: number of lines
702
+ 1: two points per line
703
+ 2: x,y pair per point
704
+
705
+ Lines are compared elementwise across the arrays (lines1[i] is compared
706
+ against lines2[i]). If one of the arrays has N=1, then that line is
707
+ compared against all lines in the other array.
708
+
709
+ Returns an array of shape (N,) where each value indicates the intercept
710
+ relative to the defined line segment. A value of 0 indicates
711
+ intersection at the first endpoint, and a value of 1 indicates
712
+ intersection at the second endpoint. Values between 1 and 0 are on the
713
+ segment, whereas values outside 1 and 0 are off of the segment.
714
+ """
715
+ # vector for each line in lines1
716
+ l1 = lines1[..., 1, :] - lines1[..., 0, :]
717
+ # vector for each line in lines2
718
+ l2 = lines2[..., 1, :] - lines2[..., 0, :]
719
+ # vector between first point of each line
720
+ diff = lines1[..., 0, :] - lines2[..., 0, :]
721
+
722
+ p = l1.copy()[..., ::-1] # vectors perpendicular to l1
723
+ p[..., 0] *= -1
724
+
725
+ f = (l2 * p).sum(axis=-1) # l2 dot p
726
+ # tempting, but bad idea!
727
+ err = np.geterr()
728
+ np.seterr(divide='ignore', invalid='ignore')
729
+ try:
730
+ h = (diff * p).sum(axis=-1) / f # diff dot p / f
731
+ finally:
732
+ np.seterr(**err)
733
+
734
+ return h
735
+
736
+ def _orientation(self, edge, point):
737
+ """Returns +1 if edge[0]->point is clockwise from edge[0]->edge[1],
738
+ -1 if counterclockwise, and 0 if parallel.
739
+ """
740
+ v1 = self.pts[point] - self.pts[edge[0]]
741
+ v2 = self.pts[edge[1]] - self.pts[edge[0]]
742
+ c = np.cross(v1, v2) # positive if v1 is CW from v2
743
+ return 1 if c > 0 else (-1 if c < 0 else 0)
744
+
745
+ def _add_tri(self, a, b, c):
746
+ # sanity check
747
+ assert a != b and b != c and c != a
748
+
749
+ # ignore flat tris
750
+ pa = self.pts[a]
751
+ pb = self.pts[b]
752
+ pc = self.pts[c]
753
+ if np.all(pa == pb) or np.all(pb == pc) or np.all(pc == pa):
754
+ return
755
+
756
+ # check this tri is unique
757
+ for t in permutations((a, b, c)):
758
+ if t in self.tris:
759
+ raise Exception("Cannot add %s; already have %s" %
760
+ ((a, b, c), t))
761
+
762
+ # TODO: should add to edges_lookup after legalization??
763
+ if self._iscounterclockwise(a, b, c):
764
+ assert (a, b) not in self._edges_lookup
765
+ assert (b, c) not in self._edges_lookup
766
+ assert (c, a) not in self._edges_lookup
767
+ self._edges_lookup[(a, b)] = c
768
+ self._edges_lookup[(b, c)] = a
769
+ self._edges_lookup[(c, a)] = b
770
+ else:
771
+ assert (b, a) not in self._edges_lookup
772
+ assert (c, b) not in self._edges_lookup
773
+ assert (a, c) not in self._edges_lookup
774
+ self._edges_lookup[(b, a)] = c
775
+ self._edges_lookup[(c, b)] = a
776
+ self._edges_lookup[(a, c)] = b
777
+
778
+ tri = (a, b, c)
779
+
780
+ self.tris[tri] = None
781
+
782
+ def _remove_tri(self, a, b, c):
783
+ for k in permutations((a, b, c)):
784
+ if k in self.tris:
785
+ break
786
+ del self.tris[k]
787
+ (a, b, c) = k
788
+
789
+ if self._edges_lookup.get((a, b), -1) == c:
790
+ del self._edges_lookup[(a, b)]
791
+ del self._edges_lookup[(b, c)]
792
+ del self._edges_lookup[(c, a)]
793
+ elif self._edges_lookup.get((b, a), -1) == c:
794
+ del self._edges_lookup[(b, a)]
795
+ del self._edges_lookup[(a, c)]
796
+ del self._edges_lookup[(c, b)]
797
+ else:
798
+ raise RuntimeError("Lost edges_lookup for tri (%d, %d, %d)" %
799
+ (a, b, c))
800
+
801
+ return k
802
+
803
+
804
+ def _triangulate_python(vertices_2d, segments):
805
+ segments = segments.reshape(len(segments) // 2, 2)
806
+ T = Triangulation(vertices_2d, segments)
807
+ T.triangulate()
808
+ vertices_2d = T.pts
809
+ triangles = T.tris.ravel()
810
+ return vertices_2d, triangles
811
+
812
+
813
+ def _triangulate_cpp(vertices_2d, segments):
814
+ import triangle
815
+ T = triangle.triangulate({'vertices': vertices_2d,
816
+ 'segments': segments}, "p")
817
+ vertices_2d = T["vertices"]
818
+ triangles = T["triangles"]
819
+ return vertices_2d, triangles
820
+
821
+
822
+ def triangulate(vertices):
823
+ """Triangulate a set of vertices.
824
+
825
+ This uses a pure Python implementation based on [1]_.
826
+
827
+ If `Triangle` by Jonathan R. Shewchuk [2]_ and the Python bindings `triangle` [3]_
828
+ are installed, this will be used instead. Users need to acknowledge and adhere to
829
+ the licensing terms of these packages.
830
+
831
+ In the VisPy `PolygonCollection Example` [4]_ a speedup of 97% using
832
+ `Triangle`/`triangle` can be achieved compared to the pure Python implementation.
833
+
834
+ Parameters
835
+ ----------
836
+ vertices : array-like
837
+ The vertices.
838
+
839
+ Returns
840
+ -------
841
+ vertices : array-like
842
+ The vertices.
843
+ triangles : array-like
844
+ The triangles.
845
+
846
+ References
847
+ ----------
848
+ .. [1] Domiter, V. and Žalik, B. Sweep‐line algorithm for constrained
849
+ Delaunay triangulation
850
+ .. [2] Shewchuk J.R. (1996) Triangle: Engineering a 2D quality mesh generator and
851
+ Delaunay triangulator. In: Lin M.C., Manocha D. (eds) Applied Computational
852
+ Geometry Towards Geometric Engineering. WACG 1996. Lecture Notes in Computer
853
+ Science, vol 1148. Springer, Berlin, Heidelberg.
854
+ https://doi.org/10.1007/BFb0014497
855
+ .. [3] https://rufat.be/triangle/
856
+ .. [4] https://github.com/vispy/vispy/blob/main/examples/collections/polygon_collection.py
857
+ """
858
+ n = len(vertices)
859
+ vertices = np.asarray(vertices)
860
+ zmean = vertices[:, 2].mean()
861
+ vertices_2d = vertices[:, :2]
862
+ segments = np.repeat(np.arange(n + 1), 2)[1:-1]
863
+ segments[-2:] = n - 1, 0
864
+
865
+ try:
866
+ import triangle # noqa: F401
867
+ except (ImportError, AssertionError):
868
+ vertices_2d, triangles = _triangulate_python(vertices_2d, segments)
869
+ else:
870
+ segments_2d = segments.reshape((-1, 2))
871
+ vertices_2d, triangles = _triangulate_cpp(vertices_2d, segments_2d)
872
+
873
+ vertices = np.empty((len(vertices_2d), 3))
874
+ vertices[:, :2] = vertices_2d
875
+ vertices[:, 2] = zmean
876
+ return vertices, triangles