styles
}
+ fn decode_style_cycles(&self) -> HashMap<&str, &StyleCycle> {
+ self.children
+ .iter()
+ .filter_map(|child| child.style_cycle())
+ .filter_map(|style_cycle| style_cycle.id.as_deref().map(|s| (s, style_cycle)))
+ .collect()
+ }
+
fn pass<'a>(
&'a self,
graph: &'a Graph,
mut csv_data: Option<DataSource>,
warn: &mut dyn FnMut(GraphWarning),
) -> Result<svg::Document, GraphError> {
- for child in &self.children {
- if let Some(style_cycle) = child.style_cycle() {
- dbg!(style_cycle);
- }
- }
let _styles = self.decode_styles();
+ let style_cycles = self.decode_style_cycles();
for delimited_source in self
.children
.iter()
}
for (graph_frame, graph) in frames.graphs() {
- document.append(self.decode_graph(graph_frame, graph, &domains, &series)?);
+ document.append(self.decode_graph(
+ graph_frame,
+ graph,
+ &domains,
+ &series,
+ &style_cycles,
+ )?);
}
Ok(document)
graph: &Graph,
domains: &BTreeMap<&str, Domain>,
series: &BTreeMap<&str, Series>,
+ style_cycles: &HashMap<&str, &StyleCycle>,
) -> Result<svg::node::element::Group, GraphError> {
let mut graph_svg = Group::new().with_id(&graph.id);
let mut bin_widths = BTreeMap::new();
Some(categoricals.insert_full(series.name.as_str(), series).0);
}
}
+ let mut styling = None;
if let Some(style_by) = element.raw.style_by.first()
&& let Some(variable) = &style_by.variable
{
.get(variable.as_str())
.ok_or_else(|| GraphError::MissingVariable(variable.clone()))?;
if series.categorical {
- categoricals.insert(series.name.as_str(), series);
+ let style_by_var_index =
+ categoricals.insert_full(series.name.as_str(), series).0;
+ if let Some(style_cycle_id) = style_by.style_cycle.as_deref()
+ && let Some(style_cycle) = style_cycles.get(style_cycle_id).copied()
+ {
+ styling = Some((style_by_var_index, style_cycle));
+ }
}
}
let categoricals = categoricals.into_values().collect_vec();
.push(values);
}
for (categoricals, group) in &grouped_data {
- let color = if let Some(color_index) = color_index {
+ let mut color = Color::BLACK;
+ if let Some(color_index) = color_index {
let index = categoricals[color_index]
.as_number()
.unwrap_or_default()
.unwrap_or_default();
let index = <usize as NumCast>::from(index).unwrap_or_default();
- PALETTE[index % PALETTE.len()]
- } else {
- Color::BLACK
- };
+ color = PALETTE[index % PALETTE.len()];
+ }
+ if let Some((index, style_cycle)) = styling {
+ let index = categoricals[index]
+ .as_number()
+ .unwrap_or_default()
+ .unwrap_or_default();
+ let index = <usize as NumCast>::from(index).unwrap_or_default();
+ let style = style_cycle.get_style(index);
+ if let Some(style_color) = style.color {
+ color = style_color;
+ }
+ }
+
fn points(
rect: &Rect,
group: &[Vec<&SeriesValue>],
struct StyleBy {
#[serde(rename = "@variable")]
variable: Option<String>,
+
+ #[serde(rename = "@styleCycle")]
+ style_cycle: Option<String>,
}
#[derive(Deserialize, Debug)]
#[derive(Clone, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct StyleCycle {
+ #[serde(rename = "@id")]
+ pub id: Option<String>,
+
#[serde(default, rename = "cycle")]
cycles: Vec<Cycle>,
}
impl StyleCycle {
- pub fn get_style(&self, mut index: usize) -> Style {
+ /// Constructs the style with the given 1-based `index` in this cycle.
+ pub fn get_style(&self, index: usize) -> Style {
+ // Treat index 0 as the last style in the cycle.
+ let mut index = index.checked_sub(1).unwrap_or_else(|| self.len() - 1);
let mut style: Option<Style> = None;
for cycle in &self.cycles {
let len = cycle.styles.len();
}
style.unwrap_or_default()
}
+
+ /// Returns the length of the cycle, that is, the number of styles before
+ /// the cycle repeats.
+ pub fn len(&self) -> usize {
+ self.cycles
+ .iter()
+ .map(|cycle| cycle.styles.len().max(1))
+ .product()
+ }
}
#[derive(Clone, Deserialize, Debug)]
Y Axis
</text>
<g id="element_simpleline_1">
-<path d="M618.75,51 L590.875,51 L563,51 L563,69.47619 L535.125,69.47619 L535.125,87.95238 L507.25,87.95238 L479.375,87.95238 L479.375,106.42857 L451.5,106.42857 L423.625,106.42857 L395.75,106.42857 L367.875,106.42857 L340,106.42857 L312.125,106.42857 L312.125,124.90476 L284.25,124.90476 L284.25,143.38095 L256.375,143.38095 L228.5,143.38095 L200.625,143.38095 L200.625,161.85715 L200.625,180.33333 L200.625,198.80952 L172.75,198.80952 L172.75,217.28572 L172.75,235.7619 L172.75,254.2381 L172.75,272.7143 L172.75,291.1905 L144.875,291.1905 L144.875,309.66666 L117,309.66666 L89.125,309.66666 L89.125,328.14285 L89.125,346.61905 L89.125,365.09525 L89.125,383.57144 L89.125,402.0476 L89.125,420.5238 L89.125,439 L61.25,439" fill="none" stroke="#000000"/>
+<path d="M618.75,51 L590.875,51 L563,51 L563,69.47619 L535.125,69.47619 L535.125,87.95238 L507.25,87.95238 L479.375,87.95238 L479.375,106.42857 L451.5,106.42857 L423.625,106.42857 L395.75,106.42857 L367.875,106.42857 L340,106.42857 L312.125,106.42857 L312.125,124.90476 L284.25,124.90476 L284.25,143.38095 L256.375,143.38095 L228.5,143.38095 L200.625,143.38095 L200.625,161.85715 L200.625,180.33333 L200.625,198.80952 L172.75,198.80952 L172.75,217.28572 L172.75,235.7619 L172.75,254.2381 L172.75,272.7143 L172.75,291.1905 L144.875,291.1905 L144.875,309.66666 L117,309.66666 L89.125,309.66666 L89.125,328.14285 L89.125,346.61905 L89.125,365.09525 L89.125,383.57144 L89.125,402.0476 L89.125,420.5238 L89.125,439 L61.25,439" fill="none" stroke="#3e58ac"/>
</g>
<g id="element_simpleline_1">
-<path d="M61.25,439 L618.75,51" fill="none" stroke="#000000"/>
+<path d="M61.25,439 L618.75,51" fill="none" stroke="#2eb848"/>
</g>
</g>
</svg>