@@ -219,6 +219,40 @@ def test_curly_vector_accepts_quiver_pivot_alias(
219219
220220 plt .close (fig )
221221
222+ @patch ("skyborn.plot.vector_plot._curly_vector_ncl" )
223+ def test_curly_vector_accepts_quiver_style_aliases (
224+ self , mock_ncl_curly , sample_vector_field
225+ ):
226+ """Matplotlib-style aliases should resolve onto canonical kwargs."""
227+ x , y , u , v = sample_vector_field
228+ color_field = np .hypot (u , v )
229+ mock_ncl_curly .return_value = Mock (spec = CurlyVectorPlotSet )
230+ fig , ax = plt .subplots (figsize = (6 , 4 ))
231+
232+ curly_vector (
233+ ax ,
234+ x ,
235+ y ,
236+ u ,
237+ v ,
238+ c = color_field ,
239+ linewidths = 2.5 ,
240+ facecolors = "gold" ,
241+ edgecolors = "navy" ,
242+ vmin = 0.1 ,
243+ vmax = 1.2 ,
244+ )
245+
246+ kwargs = mock_ncl_curly .call_args .kwargs
247+ np .testing .assert_allclose (kwargs ["color" ], color_field )
248+ assert kwargs ["linewidth" ] == pytest .approx (2.5 )
249+ assert kwargs ["facecolor" ] == "gold"
250+ assert kwargs ["edgecolor" ] == "navy"
251+ assert kwargs ["vmin" ] == pytest .approx (0.1 )
252+ assert kwargs ["vmax" ] == pytest .approx (1.2 )
253+
254+ plt .close (fig )
255+
222256 def test_curly_vector_rejects_conflicting_anchor_and_pivot (
223257 self , sample_vector_field
224258 ):
@@ -231,6 +265,55 @@ def test_curly_vector_rejects_conflicting_anchor_and_pivot(
231265
232266 plt .close (fig )
233267
268+ @pytest .mark .parametrize (
269+ ("kwargs" , "message" ),
270+ [
271+ ({"color" : "k" , "c" : "r" }, "Use only one of 'c' or 'color'" ),
272+ (
273+ {"linewidth" : 1.0 , "linewidths" : 2.0 },
274+ "Use only one of 'linewidth' or 'linewidths'" ,
275+ ),
276+ (
277+ {"facecolor" : "gold" , "facecolors" : "orange" },
278+ "Use only one of 'facecolor' or 'facecolors'" ,
279+ ),
280+ (
281+ {"edgecolor" : "k" , "edgecolors" : "face" },
282+ "Use only one of 'edgecolor' or 'edgecolors'" ,
283+ ),
284+ ],
285+ )
286+ def test_curly_vector_rejects_conflicting_style_aliases (
287+ self , sample_vector_field , kwargs , message
288+ ):
289+ """Conflicting singular/plural style aliases should fail fast."""
290+ x , y , u , v = sample_vector_field
291+ fig , ax = plt .subplots (figsize = (6 , 4 ))
292+
293+ with pytest .raises (ValueError , match = message ):
294+ curly_vector (ax , x , y , u , v , ** kwargs )
295+
296+ plt .close (fig )
297+
298+ def test_curly_vector_rejects_norm_with_vmin_vmax (self , sample_vector_field ):
299+ """Matplotlib-style normalization inputs should not be ambiguous."""
300+ x , y , u , v = sample_vector_field
301+ fig , ax = plt .subplots (figsize = (6 , 4 ))
302+
303+ with pytest .raises (ValueError , match = "Use only one of 'norm' or 'vmin'/'vmax'" ):
304+ curly_vector (
305+ ax ,
306+ x ,
307+ y ,
308+ u ,
309+ v ,
310+ c = np .hypot (u , v ),
311+ norm = mcolors .Normalize (0.0 , 1.0 ),
312+ vmin = 0.0 ,
313+ )
314+
315+ plt .close (fig )
316+
234317 def test_curly_vector_open_arrowstyle_uses_open_heads (self , sample_vector_field ):
235318 """Test that ``arrowstyle='->'`` uses open line heads."""
236319 x , y , u , v = sample_vector_field
@@ -252,6 +335,30 @@ def test_curly_vector_open_arrowstyle_uses_open_heads(self, sample_vector_field)
252335
253336 plt .close (fig )
254337
338+ def test_curly_vector_edgecolors_face_matches_facecolor (self , sample_vector_field ):
339+ """Filled heads should accept ``edgecolors='face'`` like Matplotlib."""
340+ x , y , u , v = sample_vector_field
341+ fig , ax = plt .subplots (figsize = (6 , 4 ))
342+
343+ result = curly_vector (
344+ ax ,
345+ x ,
346+ y ,
347+ u ,
348+ v ,
349+ arrowstyle = "-|>" ,
350+ density = 0.8 ,
351+ facecolors = "gold" ,
352+ edgecolors = "face" ,
353+ )
354+
355+ assert result .arrows
356+ face_rgba = np .asarray (result .arrows [0 ].get_facecolor ())
357+ edge_rgba = np .asarray (result .arrows [0 ].get_edgecolor ())
358+ np .testing .assert_allclose (face_rgba , edge_rgba )
359+
360+ plt .close (fig )
361+
255362 def test_curly_vector_filled_arrowstyle_creates_arrow_paths (
256363 self , sample_vector_field
257364 ):
0 commit comments