This document describes the complete matplotlib-compatible streamplot implementation in fortplot. The implementation follows matplotlib's algorithm exactly to ensure identical visual output and behavior.
The original streamplot implementation had several critical issues: 1. Regular grid-based seed placement - Created dense clusters without proper spacing 2. Unidirectional integration - Only integrated forward from seed points 3. No collision detection - Streamlines could overlap and cross each other 4. Poor center coverage - Circular flows showed empty centers with dense outer rings
The solution consists of three main components:
fortplot_streamline_placement.f90 - Seed generation and collision detectionfortplot_streamplot_matplotlib.f90 - Core matplotlib algorithm implementation  fortplot_figure_core.f90 - Integration with figure systemFollowing matplotlib's _gen_starting_points() function:
! Generate seeds in spiral pattern: right → up → left → down → repeat
! Starts from boundary corners, spirals inward
! Ensures boundary-first placement for better visual quality
Benefits: - Prioritizes boundary regions for better streamline coverage - Avoids clustering in interior regions - Matches matplotlib's sophisticated placement strategy
Based on matplotlib's StreamMask class:
type :: stream_mask_t
    integer :: nx, ny                    ! 30×30 base grid scaled by density
    integer, allocatable :: mask(:,:)    ! 0=free, 1=occupied
    ! Trajectory tracking for undo capability
end type
Algorithm: - 30×30 base grid scaled by density parameter (density=1 → 30×30, density=2 → 60×60) - Each mask cell can contain at most one streamline - Real-time collision detection during integration prevents overlap
Following matplotlib's integration approach:
! For each seed point:
! 1. Integrate backward (with negated velocity field)
! 2. Reset to seed point  
! 3. Integrate forward
! 4. Combine: reversed_backward + forward (excluding duplicate seed point)
Benefits: - Complete streamlines extending in both directions - Fills center regions properly (solves circular flow problem) - Matches matplotlib's comprehensive trajectory coverage
Three coordinate systems following matplotlib's DomainMap:
Transformations:
data → grid:   xg = (xd - x_min) * (nx-1) / (x_max - x_min)
grid → mask:   xm = round(xg * (mask_nx-1) / (nx-1)) + 1
mask → grid:   xg = (xm-1) * (nx-1) / (mask_nx-1)
grid → data:   xd = x_min + xg * (x_max - x_min) / (nx-1)
Following matplotlib's streamplot() function exactly:
! 1. Initialize 30×30 mask scaled by density
call mask%initialize(density)
! 2. Generate spiral seed points  
call generate_spiral_seeds([mask%nx, mask%ny], spiral_seeds, n_spiral_seeds)
! 3. For each spiral seed point:
do i = 1, n_spiral_seeds
    xm = spiral_seeds(1, i)
    ym = spiral_seeds(2, i)
    ! 4. Check if mask position is free
    if (mask%is_free(xm, ym)) then
        ! 5. Convert mask → grid coordinates
        call dmap%mask2grid(xm, ym, xg, yg)
        ! 6. Integrate bidirectional trajectory with collision detection
        call integrate_matplotlib_style(xg, yg, x, y, u, v, dmap, mask, ...)
        ! 7. Add trajectory to figure if successful
        if (success .and. n_points > 10) then
            call add_trajectory_to_figure(figure, trajectory_x, trajectory_y)
        end if
    end if
end do
Following matplotlib's trajectory integration:
! Start trajectory in mask
call mask%start_trajectory(xm, ym)
! Integrate backward direction
call integrate_direction(..., direction=-1.0, ...)
! Reset start point for forward integration  
call mask%reset_start_point(xm, ym)
! Integrate forward direction
call integrate_direction(..., direction=1.0, ...)
! Update mask during each integration step
call mask%update_trajectory(xm, ym)
Following matplotlib's approach:
! Maximum step size tied to mask resolution (like matplotlib line 548)
maxds = min(1.0_wp/mask%nx, 1.0_wp/mask%ny, 0.1_wp)
! Ensures streamlines sample every mask cell
! Prevents trajectories from skipping collision detection
Before (old implementation): - Regular grid seed placement - Empty centers in circular flows - Clustered streamlines - Unidirectional integration only
After (matplotlib-compatible):
- Spiral boundary-first seed placement
- Complete center coverage in circular flows
- Well-distributed streamlines with proper spacing
- Bidirectional integration with collision detection
thirdparty/matplotlib/lib/matplotlib/streamplot.py (lines 152-157)StreamMask for trajectory tracking and collision avoidance
Integration engine: thirdparty/matplotlib/lib/matplotlib/streamplot.py (lines 445-507)
_integrate_rk12() - Adaptive Runge-Kutta integrationmaxds parameter tied to mask resolutionReal-time mask updates during integration
Coordinate system management: thirdparty/matplotlib/lib/matplotlib/streamplot.py (lines 259-320)
DomainMap class handles three coordinate systemsGrid validation and uniform spacing requirements
Collision detection system: thirdparty/matplotlib/lib/matplotlib/streamplot.py (lines 380-435)
StreamMask class with 30×30 base grid scaled by densityUndo capability for failed trajectory attempts
Spiral seed generation: thirdparty/matplotlib/lib/matplotlib/streamplot.py (lines 104-151)
_gen_starting_points() generates boundary-first spiral patternthirdparty/pyplot-fortran/src/pyplot_module.F90Seed generation: Both use spiral boundary-first pattern
Performance characteristics:
src/fortplot_streamline_placement.f90 - StreamMask and coordinate mappingsrc/fortplot_streamplot_matplotlib.f90 - Core matplotlib algorithm  src/fortplot_figure_core.f90 - Updated streamplot() functiontest/test_streamline_placement.f90 - Placement algorithm teststest/test_matplotlib_comparison.f90 - Compatibility verificationThis implementation ensures fortplot generates streamplots that are visually identical to matplotlib, following the exact same algorithms and data structures for professional-quality scientific visualization.