Matplotlib mesh renderer: walkthrough & observations

Open
#19 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
35/100
Issue type
Documentation
Clarity
Needs clarification
Activity status
Quiet
Tech stack
matplotlib, python

Research direction

Start with src/gsp_matplotlib/renderer/matplotlib_renderer_mesh.py, especially the linked render pipeline sections, and compare the walkthrough with the implementation at commit 6f3b86c. The issue does not specify a documentation destination or acceptance criteria, so confirm the intended format and location before editing; done should mean the agreed walkthrough is placed there and matches the code.

Written by the indexing model from the issue text.

Description

How the matplotlib mesh renderer works — src/gsp_matplotlib/renderer/matplotlib_renderer_mesh.py.

Pipeline

RendererMesh.render(renderer, viewport, mesh, model_matrix, camera) returns a list of matplotlib Artists.

  1. Pull buffers (matplotlib_renderer_mesh.py:45-71) — TransBufUtils.to_buffer then Bufferx.to_numpy on positions, indices, model/view/projection matrices, face colors, edge colors, edge widths. Colors are normalized from 0-2550-1 (/ 255.0).

  2. Per-vertex → per-face broadcast (matplotlib_renderer_mesh.py:79-95) — matplotlib's PolyCollection wants one color/width per face. A local _to_per_face helper handles three cases:

    • already per-face → leave alone
    • per-vertex → pick the value at the first vertex of each triangle (array[indices[:, 0]])
    • length-1 → broadcast to face_count
  3. MVP transform (matplotlib_renderer_mesh.py:110-113) — MathUtils.compute_mvp_matrix(...) then apply_transform_matrix(vertices, mvp) → NDC. Reshape into (face_count, 3, 3) triangles.

  4. Drop Z (matplotlib_renderer_mesh.py:120) — faces_vertices_2d = faces_vertices_ndc[..., :2]. Z is kept around for sorting only.

  5. Painter's sort (matplotlib_renderer_mesh.py:146-156) — when face_sorting=True, sort faces by mean NDC z descending (far → near), and re-index colors/edge_colors/edge_widths with the same permutation. Comment notes the limitation: this works within the artist; cross-object ordering would need set_zorder and is currently disabled.

  6. Face culling (matplotlib_renderer_mesh.py:162-168) — delegates to RendererUtils.compute_faces_visible which uses the 2D cross product of edges. Sign decides CCW vs CW; magnitude < 1e-6 is treated as degenerate. FrontSide/BackSide/BothSides each pick a different predicate. Filter mask is applied to all per-face arrays.

  7. Artist cache (matplotlib_renderer_mesh.py:173-185) — one PolyCollection per mesh uuid, stored in renderer._artists. Created hidden on first render, then set_visible(True).

  8. Update artist (matplotlib_renderer_mesh.py:199-202) — set_verts, set_facecolor, set_edgecolor, set_linewidth.

Things to notice

  • uvs and normals are completely ignored — the matplotlib path is "flat triangles in screen space"; no shading, no texturing. That fits MeshBasicMaterial's name, but reinforces the open question from #18 about whether those buffers should be required at construction.
  • Per-vertex color collapses to the first vertex of each triangle — no per-face averaging, no gouraud interpolation. So an OBJ with per-vertex colors will look blocky and asymmetric (rotating the index order changes the look).
  • Culling uses the screen-space cross product, not a real normal. This is correct for triangles after a perspective divide (the sign of the 2D cross product matches the front-facing orientation), but it diverges from any culling that would use geometry.normals.
  • Mesh.sanity_check_attributes_buffer is called at matplotlib_renderer_mesh.py:101, but that method is a pass — so the call is currently free, but also free of safety. Same observation as #18.
  • Z-order across objects is commented out (matplotlib_renderer_mesh.py:187-192) — RendererUtils.update_single_artist_zorder exists but is disabled, so two overlapping meshes will paint in registration order, not depth order.
  • Camera position is never directly used — culling depends only on post-MVP geometry, so the camera enters only through the view/projection matrices.

Links pinned to commit 6f3b86c.

Dominant language
Python
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from vispy/GSP_API

All issues in vispy/GSP_API

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.