@@ -433,18 +433,58 @@ impl Axes {
433
433
}
434
434
435
435
436
- pub fn contour < ' a > ( & ' a mut self , ) -> Contour < ' a > {
436
+ /// Draw the contour lines for the data `z[j,i]` as points
437
+ /// (`x[i]`, `y[j]`).
438
+ ///
439
+ /// # Example
440
+ ///
441
+ /// ```
442
+ /// use matplotlib as plt;
443
+ /// use ndarray::{Array1, Array2};
444
+ /// let x: Array1<f64> = Array1::linspace(-1., 1., 30);
445
+ /// let y: Array1<f64> = Array1::linspace(-1., 1., 30);
446
+ /// let mut z = Array2::zeros((30, 30));
447
+ /// for (j, &y) in y.iter().enumerate() {
448
+ /// for (i, &x) in x.iter().enumerate() {
449
+ /// z[(j, i)] = (0.5 * x).powi(2) + y.powi(2);
450
+ /// }
451
+ /// }
452
+ /// let (fig, [[mut ax]]) = plt::subplots()?;
453
+ /// ax.contour(x.as_slice().unwrap(), y.as_slice().unwrap(), &z).plot();
454
+ /// fig.save().to_file("target/contour.pdf")?;
455
+ /// # Ok::<(), matplotlib::Error>(())
456
+ /// ```
457
+ pub fn contour < ' a , D > (
458
+ & ' a mut self , x : D , y : D , z : & ' a ndarray:: Array2 < f64 > ,
459
+ ) -> Contour < ' a , D >
460
+ where D : AsRef < [ f64 ] > {
437
461
Contour {
438
462
axes : self ,
439
463
options : PlotOptions :: new ( ) ,
464
+ x, y, z,
465
+ levels : None ,
440
466
}
441
467
}
442
468
469
+ /// Draw the contour lines for function `f` in the rectangle `ab`×`cd`.
470
+ ///
471
+ /// # Example
472
+ ///
473
+ /// ```
474
+ /// use matplotlib as plt;
475
+ /// let (fig, [[mut ax]]) = plt::subplots()?;
476
+ /// ax.contour_fun([-1., 1.], [-1., 1.], |x, y| {
477
+ /// (0.5 * x).powi(2) + y.powi(2)
478
+ /// })
479
+ /// .plot();
480
+ /// fig.save().to_file("target/contour_fun.pdf")?;
481
+ /// # Ok::<(), matplotlib::Error>(())
482
+ /// ```
443
483
pub fn contour_fun < ' a , F > (
444
484
& ' a mut self ,
445
- f : F ,
446
485
ab : [ f64 ; 2 ] ,
447
486
cd : [ f64 ; 2 ] ,
487
+ f : F ,
448
488
) -> ContourFun < ' a , F >
449
489
where F : FnMut ( f64 , f64 ) -> f64 {
450
490
ContourFun {
@@ -453,6 +493,7 @@ impl Axes {
453
493
f, ab, cd,
454
494
n1 : 100 ,
455
495
n2 : 100 ,
496
+ levels : None ,
456
497
}
457
498
}
458
499
@@ -822,16 +863,51 @@ where F: FnMut(f64) -> Y,
822
863
}
823
864
}
824
865
866
+ pub struct QuadContourSet {
867
+ contours : PyObject ,
868
+ }
869
+
870
+ impl QuadContourSet {
871
+ pub fn set_color ( & mut self , c : impl Color ) -> & mut Self {
872
+ Python :: with_gil ( |py| {
873
+ meth ! ( self . contours, set_color, ( colors:: py( py, c) , ) ) . unwrap ( )
874
+ } ) ;
875
+ self
876
+ }
877
+ }
825
878
826
879
#[ must_use]
827
- pub struct Contour < ' a > {
880
+ pub struct Contour < ' a , D > {
828
881
axes : & ' a Axes ,
829
882
options : PlotOptions < ' a > ,
883
+ x : D ,
884
+ y : D ,
885
+ z : & ' a ndarray:: Array2 < f64 > ,
886
+ levels : Option < & ' a [ f64 ] > ,
830
887
}
831
888
832
- impl < ' a > Contour < ' a > {
889
+ impl < ' a , D > Contour < ' a , D >
890
+ where D : AsRef < [ f64 ] > {
833
891
set_plotoptions ! ( ) ;
834
892
893
+ pub fn plot ( & self ) -> QuadContourSet {
894
+ Python :: with_gil ( |py| {
895
+ let x = self . x . as_ref ( ) . to_pyarray_bound ( py) ;
896
+ let y = self . y . as_ref ( ) . to_pyarray_bound ( py) ;
897
+ let z = self . z . to_pyarray_bound ( py) ;
898
+ let opt = self . options . kwargs ( py) ;
899
+ if let Some ( levels) = self . levels {
900
+ let levels = levels. to_pyarray_bound ( py) ;
901
+ opt. set_item ( "levels" , levels) . unwrap ( ) ;
902
+ }
903
+ let contours = self . axes . ax
904
+ . call_method_bound ( py, intern ! ( py, "contour" ) ,
905
+ ( x, y, z) ,
906
+ Some ( & opt) )
907
+ . unwrap ( ) ;
908
+ QuadContourSet { contours }
909
+ } )
910
+ }
835
911
}
836
912
837
913
@@ -842,14 +918,51 @@ pub struct ContourFun<'a, F> {
842
918
f : F ,
843
919
ab : [ f64 ; 2 ] ,
844
920
cd : [ f64 ; 2 ] ,
845
- n1 : usize ,
921
+ n1 : usize , // FIXME: want to be more versatile than an equispaced grid?
846
922
n2 : usize ,
923
+ levels : Option < & ' a [ f64 ] > ,
847
924
}
848
925
849
926
impl < ' a , F > ContourFun < ' a , F >
850
927
where F : FnMut ( f64 , f64 ) -> f64 {
851
928
set_plotoptions ! ( ) ;
852
929
930
+ pub fn plot ( & mut self ) -> QuadContourSet {
931
+ let mut x = Vec :: with_capacity ( self . n1 ) ;
932
+ let mut y = Vec :: with_capacity ( self . n2 ) ;
933
+ let mut z = ndarray:: Array2 :: zeros ( ( self . n2 , self . n1 ) ) ;
934
+ let a = self . ab [ 0 ] ;
935
+ let dx = ( self . ab [ 1 ] - a) / ( self . n1 - 1 ) as f64 ;
936
+ for i in 0 .. self . n1 {
937
+ x. push ( a + dx * i as f64 ) ;
938
+ }
939
+ let c = self . cd [ 0 ] ;
940
+ let dy = ( self . cd [ 1 ] - c) / ( self . n2 - 1 ) as f64 ;
941
+ for j in 0 .. self . n2 {
942
+ y. push ( c + dy * j as f64 ) ;
943
+ }
944
+ for ( j, & y) in y. iter ( ) . enumerate ( ) {
945
+ for ( i, & x) in x. iter ( ) . enumerate ( ) {
946
+ z[ ( j, i) ] = ( self . f ) ( x, y) ;
947
+ }
948
+ }
949
+ Python :: with_gil ( |py| {
950
+ let x = x. to_pyarray_bound ( py) ;
951
+ let y = y. to_pyarray_bound ( py) ;
952
+ let z = z. to_pyarray_bound ( py) ;
953
+ let opt = self . options . kwargs ( py) ;
954
+ if let Some ( levels) = self . levels {
955
+ let levels = levels. to_pyarray_bound ( py) ;
956
+ opt. set_item ( "levels" , levels) . unwrap ( ) ;
957
+ }
958
+ let contours = self . axes . ax
959
+ . call_method_bound ( py, intern ! ( py, "contour" ) ,
960
+ ( x, y, z) ,
961
+ Some ( & opt) )
962
+ . unwrap ( ) ;
963
+ QuadContourSet { contours }
964
+ } )
965
+ }
853
966
}
854
967
855
968
@@ -871,8 +984,7 @@ impl Line2D {
871
984
/// Set the color of the line to `c`.
872
985
pub fn set_color ( & mut self , c : impl Color ) -> & mut Self {
873
986
Python :: with_gil ( |py| {
874
- let c = PyTuple :: new_bound ( py, c. rgba ( ) ) ;
875
- meth ! ( self . line2d, set_color, ( c, ) ) . unwrap ( ) ;
987
+ meth ! ( self . line2d, set_color, ( colors:: py( py, c) , ) ) . unwrap ( ) ;
876
988
self
877
989
} )
878
990
}
0 commit comments