1+ """
2+ fortplot.advanced - Advanced plotting functions
3+ ===============================================
4+
5+ Advanced plotting capabilities including contour plots, streamplots,
6+ and pcolormesh for the fortplot Python interface.
7+ """
8+
9+ import numpy as np
10+ import fortplot .fortplot_wrapper as _fortplot
11+
12+ def _ensure_array (obj ):
13+ """Convert input to numpy array if not already an array (DRY helper)."""
14+ if not isinstance (obj , np .ndarray ):
15+ return np .array (obj )
16+ return obj
17+
18+ def contour (X , Y , Z , levels = None ):
19+ """Draw contour lines.
20+
21+ Parameters
22+ ----------
23+ X, Y : array-like
24+ The coordinates of the values in Z.
25+ Z : array-like
26+ The height values over which the contour is drawn.
27+ levels : array-like, optional
28+ Determines the number and positions of the contour lines.
29+ """
30+ X = _ensure_array (X )
31+ Y = _ensure_array (Y )
32+ Z = _ensure_array (Z )
33+
34+ # Extract 1D coordinate arrays from 2D meshgrid (if needed)
35+ if X .ndim == 2 :
36+ x = X [0 , :] # First row
37+ else :
38+ x = X
39+ if Y .ndim == 2 :
40+ y = Y [:, 0 ] # First column
41+ else :
42+ y = Y
43+
44+ # Transpose Z for Fortran column-major order
45+ z = Z .T .copy ()
46+
47+ if levels is None :
48+ _fortplot .fortplot .contour (x , y , z )
49+ else :
50+ levels = _ensure_array (levels )
51+ _fortplot .fortplot .contour (x , y , z , levels )
52+
53+ def contourf (X , Y , Z , levels = None , ** kwargs ):
54+ """Draw filled contours.
55+
56+ Parameters
57+ ----------
58+ X, Y : array-like
59+ The coordinates of the values in Z.
60+ Z : array-like
61+ The height values over which the contour is drawn.
62+ levels : array-like, optional
63+ Determines the number and positions of the contour lines.
64+ """
65+ X = _ensure_array (X )
66+ Y = _ensure_array (Y )
67+ Z = _ensure_array (Z )
68+
69+ # Extract 1D coordinate arrays from 2D meshgrid (if needed)
70+ if X .ndim == 2 :
71+ x = X [0 , :] # First row
72+ else :
73+ x = X
74+ if Y .ndim == 2 :
75+ y = Y [:, 0 ] # First column
76+ else :
77+ y = Y
78+
79+ # Transpose Z for Fortran column-major order
80+ z = Z .T .copy ()
81+
82+ if levels is None :
83+ _fortplot .fortplot .contour_filled (x , y , z )
84+ else :
85+ levels = _ensure_array (levels )
86+ _fortplot .fortplot .contour_filled (x , y , z , levels )
87+
88+ def streamplot (X , Y , U , V , density = 1.0 , ** kwargs ):
89+ """Draw streamlines of a vector flow.
90+
91+ Parameters
92+ ----------
93+ X, Y : array-like
94+ The coordinates of the values in U, V.
95+ U, V : array-like
96+ x and y-velocities. Number of rows should match length of Y, and
97+ the number of columns should match X.
98+ density : float, optional
99+ Controls the closeness of streamlines. Default is 1.
100+ """
101+ X = _ensure_array (X )
102+ Y = _ensure_array (Y )
103+ U = _ensure_array (U )
104+ V = _ensure_array (V )
105+
106+ # Extract 1D coordinate arrays from 2D meshgrid (if needed)
107+ if X .ndim == 2 :
108+ x = X [0 , :] # First row
109+ else :
110+ x = X
111+ if Y .ndim == 2 :
112+ y = Y [:, 0 ] # First column
113+ else :
114+ y = Y
115+
116+ # Transpose U, V for Fortran column-major order
117+ u = U .T .copy ()
118+ v = V .T .copy ()
119+
120+ _fortplot .fortplot .streamplot (x , y , u , v , density )
121+
122+ def pcolormesh (X , Y , C , cmap = None , vmin = None , vmax = None , edgecolors = 'none' , linewidths = None , ** kwargs ):
123+ """Create a pseudocolor plot with a non-regular rectangular grid.
124+
125+ Parameters
126+ ----------
127+ X, Y : array-like
128+ The coordinates of the quadrilateral corners. Can be:
129+ - 1D arrays of length N+1 and M+1 for regular grid
130+ - 2D arrays of shape (M+1, N+1) for irregular grid
131+ C : array-like
132+ The color values. Shape (M, N).
133+ cmap : str or Colormap, optional
134+ The colormap to use. Supported: 'viridis', 'plasma', 'inferno',
135+ 'coolwarm', 'jet', 'crest' (default).
136+ vmin, vmax : float, optional
137+ Data range for colormap normalization.
138+ edgecolors : color or 'none', optional
139+ Color of the edges. Default 'none'.
140+ linewidths : float, optional
141+ Width of the edges.
142+ **kwargs
143+ Additional keyword arguments (for matplotlib compatibility).
144+
145+ Returns
146+ -------
147+ QuadMesh
148+ The matplotlib QuadMesh collection (placeholder for compatibility).
149+
150+ Examples
151+ --------
152+ Basic usage with regular grid:
153+
154+ >>> x = np.linspace(0, 1, 11)
155+ >>> y = np.linspace(0, 1, 8)
156+ >>> C = np.random.random((7, 10))
157+ >>> pcolormesh(x, y, C, cmap='viridis')
158+
159+ With custom color limits:
160+
161+ >>> pcolormesh(x, y, C, cmap='plasma', vmin=0.2, vmax=0.8)
162+ """
163+ X = _ensure_array (X )
164+ Y = _ensure_array (Y )
165+ C = _ensure_array (C )
166+
167+ # Handle 1D coordinate arrays (regular grid case)
168+ if X .ndim == 1 and Y .ndim == 1 :
169+ x = X
170+ y = Y
171+ elif X .ndim == 2 and Y .ndim == 2 :
172+ # Irregular grid support: validate grid structure and extract coordinates
173+ if X .shape != Y .shape :
174+ raise ValueError ("For irregular grids, X and Y must have identical shapes" )
175+ if X .shape [0 ] != C .shape [0 ] + 1 or X .shape [1 ] != C .shape [1 ] + 1 :
176+ raise ValueError ("For irregular grids, coordinate arrays must be (M+1, N+1) for data shape (M, N)" )
177+
178+ # For irregular grids, we need to pass the full coordinate arrays
179+ # However, the current Fortran interface expects 1D arrays
180+ # Extract boundary coordinates as a reasonable approximation
181+ # This preserves the overall grid bounds while maintaining compatibility
182+ x = X [0 , :] # First row (bottom edge)
183+ y = Y [:, 0 ] # First column (left edge)
184+
185+ # Note: Full irregular grid rendering would require modifying the Fortran interface
186+ # to accept 2D coordinate arrays and implement curvilinear grid interpolation
187+ else :
188+ raise ValueError ("X and Y must have the same dimensionality (both 1D or both 2D)" )
189+
190+ # Transpose C for Fortran column-major order
191+ c = C .T .copy ()
192+
193+ # Set default colormap if not specified
194+ if cmap is None :
195+ cmap = 'viridis'
196+
197+ # Call Fortran function with optional arguments
198+ if vmin is not None and vmax is not None :
199+ if edgecolors != 'none' and linewidths is not None :
200+ _fortplot .fortplot .pcolormesh (x , y , c , cmap , vmin , vmax , edgecolors , linewidths )
201+ elif edgecolors != 'none' :
202+ _fortplot .fortplot .pcolormesh (x , y , c , cmap , vmin , vmax , edgecolors )
203+ else :
204+ _fortplot .fortplot .pcolormesh (x , y , c , cmap , vmin , vmax )
205+ elif vmin is not None :
206+ _fortplot .fortplot .pcolormesh (x , y , c , cmap , vmin )
207+ elif cmap != 'viridis' :
208+ _fortplot .fortplot .pcolormesh (x , y , c , cmap )
209+ else :
210+ _fortplot .fortplot .pcolormesh (x , y , c )
211+
212+ # Return placeholder object for matplotlib compatibility
213+ class QuadMeshPlaceholder :
214+ """Minimal matplotlib QuadMesh compatibility placeholder."""
215+ def __init__ (self ):
216+ self .colorbar = None
217+ self .figure = None
218+
219+ def get_clim (self ):
220+ """Get the color limits (placeholder)."""
221+ return (vmin , vmax ) if vmin is not None and vmax is not None else (0 , 1 )
222+
223+ def set_clim (self , vmin_new = None , vmax_new = None ):
224+ """Set the color limits (placeholder)."""
225+ pass
226+
227+ return QuadMeshPlaceholder ()
0 commit comments