@@ -433,18 +433,58 @@ impl Axes {
433433 }
434434
435435
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 ] > {
437461 Contour {
438462 axes : self ,
439463 options : PlotOptions :: new ( ) ,
464+ x, y, z,
465+ levels : None ,
440466 }
441467 }
442468
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+ /// ```
443483 pub fn contour_fun < ' a , F > (
444484 & ' a mut self ,
445- f : F ,
446485 ab : [ f64 ; 2 ] ,
447486 cd : [ f64 ; 2 ] ,
487+ f : F ,
448488 ) -> ContourFun < ' a , F >
449489 where F : FnMut ( f64 , f64 ) -> f64 {
450490 ContourFun {
@@ -453,6 +493,7 @@ impl Axes {
453493 f, ab, cd,
454494 n1 : 100 ,
455495 n2 : 100 ,
496+ levels : None ,
456497 }
457498 }
458499
@@ -822,16 +863,51 @@ where F: FnMut(f64) -> Y,
822863 }
823864}
824865
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+ }
825878
826879#[ must_use]
827- pub struct Contour < ' a > {
880+ pub struct Contour < ' a , D > {
828881 axes : & ' a Axes ,
829882 options : PlotOptions < ' a > ,
883+ x : D ,
884+ y : D ,
885+ z : & ' a ndarray:: Array2 < f64 > ,
886+ levels : Option < & ' a [ f64 ] > ,
830887}
831888
832- impl < ' a > Contour < ' a > {
889+ impl < ' a , D > Contour < ' a , D >
890+ where D : AsRef < [ f64 ] > {
833891 set_plotoptions ! ( ) ;
834892
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+ }
835911}
836912
837913
@@ -842,14 +918,51 @@ pub struct ContourFun<'a, F> {
842918 f : F ,
843919 ab : [ f64 ; 2 ] ,
844920 cd : [ f64 ; 2 ] ,
845- n1 : usize ,
921+ n1 : usize , // FIXME: want to be more versatile than an equispaced grid?
846922 n2 : usize ,
923+ levels : Option < & ' a [ f64 ] > ,
847924}
848925
849926impl < ' a , F > ContourFun < ' a , F >
850927where F : FnMut ( f64 , f64 ) -> f64 {
851928 set_plotoptions ! ( ) ;
852929
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+ }
853966}
854967
855968
@@ -871,8 +984,7 @@ impl Line2D {
871984 /// Set the color of the line to `c`.
872985 pub fn set_color ( & mut self , c : impl Color ) -> & mut Self {
873986 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 ( ) ;
876988 self
877989 } )
878990 }
0 commit comments