Skip to content

Commit 02128f4

Browse files
committed
Add basic stairs plots
1 parent 89a9c6b commit 02128f4

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

examples/plot_types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ fn main() -> anyhow::Result<()> {
1717
stem()?;
1818
fill_between()?;
1919
stackplot()?;
20+
stairs()?;
2021
Ok(())
2122
}
2223

@@ -128,3 +129,16 @@ fn stackplot() -> anyhow::Result<()> {
128129
fig.save().to_file(format!("{BASE}stackplot.pdf"))?;
129130
Ok(())
130131
}
132+
133+
fn stairs() -> anyhow::Result<()> {
134+
let y = [4.8, 5.5, 3.5, 4.6, 6.5, 6.6, 2.6, 3.0];
135+
136+
let (fig, [[mut ax]]) = figure::subplots()?;
137+
138+
ax.stairs(&y).linewidth(2.5).plot();
139+
140+
ax.set_xlim(0., 8.) .set_xticks((1..8).map(f64::from));
141+
ax.set_ylim(0., 8.) .set_yticks((1..8).map(f64::from));
142+
fig.save().to_file(format!("{BASE}stairs.pdf"))?;
143+
Ok(())
144+
}

src/axes.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,19 @@ impl Axes {
290290
Stack::new(self, x, y)
291291
}
292292

293+
/// Draw a stepwise constant function as a line or a filled plot.
294+
///
295+
/// `values` is the function values between these steps.
296+
/// Depending on [`fill`][Stairs::fill], the function is drawn
297+
/// either as a continuous line with vertical segments at the
298+
/// edges, or as a filled area.
299+
pub fn stairs<'a>(
300+
&'a mut self,
301+
values: &'a impl Vector<f64>,
302+
) -> Stairs<'a> {
303+
Stairs::new(self, values)
304+
}
305+
293306
/// Set the title to `txt` for the Axes.
294307
pub fn set_title(&mut self, txt: impl AsRef<str>) -> &mut Self {
295308
meth!(self.ax, set_title, (txt.as_ref(),)).unwrap();
@@ -1241,6 +1254,85 @@ impl<'a> Stack<'a> {
12411254
}
12421255
}
12431256

1257+
pub struct Stairs<'a> {
1258+
axes: &'a Axes,
1259+
y: &'a dyn Vector<f64>,
1260+
edges: Option<&'a dyn Vector<f64>>,
1261+
orientation: Orientation,
1262+
fill: bool,
1263+
// FIXME: there are more optional arguments.
1264+
linewidth: Option<f64>,
1265+
}
1266+
1267+
#[derive(Debug, Clone, Copy)]
1268+
pub enum Orientation {
1269+
Horizontal,
1270+
Vertical,
1271+
}
1272+
1273+
impl Orientation {
1274+
fn as_str(self) -> &'static str {
1275+
match self {
1276+
Orientation::Horizontal => "horizontal",
1277+
Orientation::Vertical => "vertical",
1278+
}
1279+
}
1280+
}
1281+
1282+
impl<'a> Stairs<'a> {
1283+
fn new(
1284+
axes: &'a Axes,
1285+
y: &'a impl Vector<f64>,
1286+
) -> Self {
1287+
Self {
1288+
axes, y,
1289+
edges: None,
1290+
orientation: Orientation::Vertical,
1291+
fill: false,
1292+
linewidth: None,
1293+
}
1294+
}
1295+
1296+
/// Define the x-axis positions of the steps.
1297+
pub fn edges(mut self, x: &'a impl Vector<f64>) -> Self {
1298+
self.edges = Some(x);
1299+
self
1300+
}
1301+
1302+
pub fn orientation(mut self, o: Orientation) -> Self {
1303+
self.orientation = o;
1304+
self
1305+
}
1306+
1307+
pub fn fill(mut self) -> Self {
1308+
self.fill = true;
1309+
self
1310+
}
1311+
1312+
pub fn linewidth(mut self, lw: f64) -> Self {
1313+
self.linewidth = Some(lw);
1314+
self
1315+
}
1316+
1317+
pub fn plot(self) {
1318+
Python::attach(|py| {
1319+
let y = self.y.to_pyvector(py);
1320+
let edges = self.edges.map(|e| e.to_pyvector(py));
1321+
let kwargs = PyDict::new(py);
1322+
kwargs.set_item("orientation", self.orientation.as_str()).unwrap();
1323+
kwargs.set_item("fill", self.fill).unwrap();
1324+
if let Some(lw) = self.linewidth {
1325+
kwargs.set_item("linewidth", lw).unwrap();
1326+
}
1327+
self.axes.ax.bind(py)
1328+
.call_method(intern!(py, "stairs"),
1329+
(y, edges),
1330+
Some(&kwargs))
1331+
.unwrap();
1332+
})
1333+
}
1334+
}
1335+
12441336
pub struct QuadContourSet {
12451337
contours: Py<PyAny>,
12461338
}

0 commit comments

Comments
 (0)