axis_domains[axis] = AxisDomain {
domain,
formatter: formatter.unwrap_or_else(|| Formatter::default_for_locale(locale)),
+ axis: axis2,
label: dbg!(axis2.and_then(|axis| axis.label.as_ref())),
major_ticks: axis2.and_then(|axis| axis.major_ticks.as_ref()),
};
let heading = FacetHeadings::new(
Side::default_for_axis(axis),
&faceting[axis],
+ &axis_domains[axis.into()],
rect.0[axis].length() - heading_heights[!axis],
graph.axis_dimension(axis),
&mut font_metrics,
format!("rotate(90 {x} {y}) translate({x} {y})")
});
- for (x_index, _x_facet) in x_facets.clone().enumerate() {
- {
- let rect = inner.subrect(x_index, nx_facets, ny_facets - 1, ny_facets);
- // X axis.
- let y = rect.0[Axis2::Y].1;
- let x_span = rect.0[Axis2::X];
- /*graph_svg.append(
- Line::new()
- .set("x1", x_span.0)
- .set("x2", x_span.1)
- .set("y1", y)
- .set("y2", y)
- .set("stroke", "black"),
- );*/
- for (frac, label) in axis_domains[Axis3::X].ticks() {
- let svg_x = rect.0[Axis2::X].frac(frac);
- graph_svg = graph_svg
- .add(
- Line::new()
- .set("x1", svg_x)
- .set("x2", svg_x)
- .set("y1", y)
- .set("y2", y + 10.0)
- .set("stroke", "black"),
- )
- .add(
- svg::node::element::Text::new(dbg!(&label))
- .set("x", svg_x)
- .set("y", y + 10.0)
- .set("dominant-baseline", "hanging")
- .set("text-anchor", "middle"),
- );
- }
- }
- }
-
- for (y_index, _y_facet) in y_facets.clone().enumerate() {
- let subrect = inner.subrect(0, nx_facets, y_index, ny_facets);
- // Y axis.
- let x = subrect.0[Axis2::X].0;
- let y_span = subrect.0[Axis2::Y];
- /*graph_svg.append(
- Line::new()
- .set("y1", y_span.0)
- .set("y2", y_span.1)
- .set("x1", x)
- .set("x2", x)
- .set("stroke", "black")
- .set("c:description", format!("Y axis {y_index}")),
- );*/
- for (frac, label) in axis_domains[Axis3::Y].ticks() {
- let svg_y = subrect.0[Axis2::Y].frac(1.0 - frac);
- graph_svg = graph_svg
- .add(
- Line::new()
- .set("y1", svg_y)
- .set("y2", svg_y)
- .set("x1", x)
- .set("x2", x - 10.0)
- .set("stroke", "black"),
- )
- .add(
- svg::node::element::Text::new(label)
- .set("y", svg_y)
- .set("x", x - 10.0)
- .set("dominant-baseline", "middle")
- .set("text-anchor", "end"),
- );
- }
- }
-
for (x_index, x_facet) in x_facets.enumerate() {
for (y_index, y_facet) in y_facets.clone().enumerate() {
let rect = inner.subrect(x_index, nx_facets, y_index, ny_facets);
graph_svg.append(bottom);
graph_svg.append(left);
graph_svg.append(right);
- //graph_svg.append(rect.as_svg().set("fill", "none").set("stroke", "red"));
- //graph_svg.append(inner.as_svg().set("fill", "none").set("stroke", "green"));
+ graph_svg.append(rect.as_svg().set("fill", "none").set("stroke", "red"));
+ graph_svg.append(inner.as_svg().set("fill", "none").set("stroke", "green"));
Ok(graph_svg)
}
}
struct AxisDomain<'a> {
domain: Domain,
formatter: Formatter,
+ axis: Option<&'a Axis>,
label: Option<&'a Label>,
major_ticks: Option<&'a MajorTicks>,
}
Self {
domain: Domain::default(),
formatter: Formatter::default_for_locale(locale),
+ axis: None,
label: None,
major_ticks: None,
}
/// Width, fixed by the caller.
width: f64,
- /// Axes and their labels.
+ /// Axis labels (e.g. X-axis or Y-axis label).
///
/// Ordinarily there is at most one axis on each side; if there happens to
/// be more than one then these go from innermost to outermost. `axes`
/// appear inside `facets` if both are present on the same side.
axes: Vec<FacetAxisHeight<'a>>,
+ /// Hierarchical facet labels (e.g. the value for each facet, stacked if
+ /// there is more than one level of facets on an axis).
+ ///
/// Per-row heights on the default (left or top) side and the opposite side,
/// calculated based on `axis_facets` and `width`.
///
/// From innermost to outermost.
facets: Vec<FacetRowHeight<'a>>,
+ /// Axis tick marks.
+ ticks: Option<(Side, Vec<(f64, String)>, f64)>,
+
/// Number of facets.
n_facets: usize,
+
+ /// [AxisDomain] for X or Y axis.
+ ///
+ /// Currently there's always exactly one of these for an axis, although
+ /// perhaps there should be up to 2.
+ axis_domain: &'a AxisDomain<'a>,
}
struct FacetAxisHeight<'a> {
.filter(|row_height| row_height.side == side)
.map(|row_height| row_height.height())
.sum::<f64>()
+ + if let Some((ticks_side, _, height)) = &self.ticks
+ && *ticks_side == side
+ {
+ *height
+ } else {
+ 0.0
+ }
}
fn new(
default_side: Side,
facets: &'a [Facet<'a>],
+ axis_domain: &'a AxisDomain<'a>,
width: f64,
dimension: Option<&'a Dimension>,
font_metrics: &mut FontMetrics,
}
})
.collect();
+ let ticks = axis_domain.axis.map(|axis| {
+ (
+ if axis.opposite {
+ !default_side
+ } else {
+ default_side
+ },
+ axis_domain.ticks(),
+ 10.0,
+ )
+ });
Self {
width,
axes,
+ axis_domain,
+ ticks,
facets,
n_facets,
}
let span = Span(0.0, self.width).subdivide(self.n_facets, 5.0);
let mut group = Group::new();
+ fn render_ticks(
+ this: &FacetHeadings,
+ ticks: &[(f64, String)],
+ group: &mut Group,
+ span: &DividedSpan,
+ y: f64,
+ ) {
+ for facet_span in (0..this.n_facets).map(|index| span.nth(index)) {
+ for (frac, label) in ticks {
+ let x = facet_span.frac(*frac);
+ group.append(
+ Line::new()
+ .set("x1", x)
+ .set("x2", x)
+ .set("y1", y)
+ .set("y2", y + 10.0)
+ .set("stroke", "black"),
+ );
+ group.append(
+ svg::node::element::Text::new(dbg!(label))
+ .set("x", x)
+ .set("y", y + 10.0)
+ .set("dominant-baseline", "hanging")
+ .set("text-anchor", "middle"),
+ );
+ }
+ }
+ }
+
+ let mut y = 0.0;
+ if let Some((ticks_side, ticks, height)) = &self.ticks
+ && side == *ticks_side
+ && side == Side::Bottom
+ {
+ render_ticks(self, ticks, &mut group, &span, y);
+ y += height;
+ }
/*
group.append(
Rect(enum_map! {
.set("fill", "none")
.set("stroke", "red"),
);*/
- let mut y = 0.0;
for axis in self.axes.iter().filter(|axis| axis.side == side) {
if dbg!(axis.axis.combine_axis_labels) {
group.append(
}
y += height.facet_label_height;
}
+ if let Some((ticks_side, ticks, height)) = &self.ticks
+ && *ticks_side == side
+ && side == Side::Top
+ {
+ render_ticks(self, ticks, &mut group, &span, y);
+ y += height;
+ }
group
}
}