Skip to content

Conversation

@scottshambaugh
Copy link
Contributor

@scottshambaugh scottshambaugh commented Jan 19, 2026

PR summary

A collection of speedups for 3d plotting, from playing around with the py-spy profiler today.

  • Prefer filling arrays with nans directly, instead of masked arrays that later get filled
  • Move _get_coord_info from axis3d to axes3d, since there's nothing axis-specific in it
  • Cache axes3d calculations that are used multiple times per draw by each axis: scaled_limits, bounds, transformed_cube, and coord_info.
  • Call axis._update_ticks() once per axis at start of draw, instead of multiple times in methods
  • Batch the axis tick 3d projections rather than calling once per tick

Waiting on #30980 as a dependency. Diff to that is here:
https://github.com/matplotlib/matplotlib/compare/4ab0014840c3e86b3ca820dba7531857cb168ed6..scottshambaugh:3d_draw_speed

I measure a ~20-30% speedup when executing this script, which rotates the plot to execute 100 draws:

import time
import numpy as np
import matplotlib.pyplot as plt

print("Starting...")

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

x = y = np.linspace(-1, 1, 1000)
X, Y = np.meshgrid(x, y)
Z = X ** 2 + Y ** 2

print("Timing...")

start_time = time.perf_counter()
ax.plot_wireframe(X, Y, Z, axlim_clip=False)
ax.set_xlim(0, 1)
ax.set_xscale('symlog')
ax.set_yscale('symlog')
for i in range(100):
    ax.view_init(elev=i, azim=i)
    fig.canvas.draw()
end_time = time.perf_counter()

plt.close()

print(f"Time taken: {end_time - start_time:.4f} seconds")

PR checklist

scottshambaugh and others added 15 commits January 16, 2026 22:19
This fixes a long-standing issue where 3D plots ignored scale transforms.
The methods set_xscale(), set_yscale(), set_zscale() existed but the
transforms were never applied to data or coordinates.

Key changes:

- axes3d.py: Add _get_scaled_limits() to transform axis limits through
  scale transforms. Modify get_proj() to use scaled limits for world
  transformation. Override _update_transScale() to use identity transforms
  since 3D projection handles scales internally. Update autoscale_view()
  and _set_lim3d() to apply margins in transformed space.

- art3d.py: Add _apply_scale_transforms() utility function. Update all
  do_3d_projection() methods to apply scale transforms before projection.

- axis3d.py: Update _get_coord_info() to return scaled-space bounds.
  Modify _draw_ticks() to transform tick locations to scaled space.
  Update draw() and draw_grid() for proper coordinate handling.

The fix ensures that:
- Data coordinates are transformed through scale transforms before
  projection (e.g., log10 for log scale)
- World transformation matrix maps scaled coordinates to unit cube
- Axis ticks, labels, and grid lines position correctly
- 2D display transforms remain linear (no double-transformation)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions github-actions bot added the Documentation: examples files in galleries/examples label Jan 19, 2026
@scottshambaugh scottshambaugh removed the Documentation: examples files in galleries/examples label Jan 19, 2026
@github-actions github-actions bot added the Documentation: examples files in galleries/examples label Jan 19, 2026
@scottshambaugh scottshambaugh removed the Documentation: examples files in galleries/examples label Jan 19, 2026
@github-actions github-actions bot added the Documentation: examples files in galleries/examples label Jan 19, 2026
@scottshambaugh scottshambaugh changed the title [PERF] 3d draw speedups PERF: 3d draw speedups Jan 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant