use crate::kvasir::node::{ExecutionContext, KvasirNode};
use crate::kvasir::resource::ResourceId;
use crate::kvasir::nodes::RES_SCENE;
pub const RES_FILTER_TEMP_A: ResourceId = ResourceId(100);
pub const RES_FILTER_TEMP_B: ResourceId = ResourceId(101);
pub const RES_FILTER_INPUT: ResourceId = ResourceId(102);
pub struct SvgFilterNode {
input: ResourceId,
output: ResourceId,
input_slice: [ResourceId; 1],
output_slice: [ResourceId; 1],
filter_graph: Option<crate::svg_filter_graph::SvgFilterGraph>,
label: &'static str,
}
impl SvgFilterNode {
pub fn new(input: ResourceId, output: ResourceId) -> Self {
Self {
input,
output,
input_slice: [input; 1],
output_slice: [output; 1],
filter_graph: None,
label: "SvgFilter",
}
}
pub fn with_filter_graph(mut self, graph: crate::svg_filter_graph::SvgFilterGraph) -> Self {
self.filter_graph = Some(graph);
self
}
pub fn with_label(mut self, label: &'static str) -> Self {
self.label = label;
self
}
}
impl KvasirNode for SvgFilterNode {
fn label(&self) -> &'static str {
self.label
}
fn inputs(&self) -> &[ResourceId] {
&self.input_slice
}
fn outputs(&self) -> &[ResourceId] {
&self.output_slice
}
fn pass_id(&self) -> crate::kvasir::nodes::PassId {
crate::kvasir::nodes::PassId::PostProcess {
pipeline_id: 0xF1_000,
}
}
fn execute(&self, ctx: &mut ExecutionContext) {
if self.filter_graph.is_none() {
return;
}
log::trace!(
"[Kvasir] Executing SVG filter: {} -> {:?}",
self.label,
self.output
);
}
}
pub struct SvgFilterGraphBuilder {
nodes: Vec<(ResourceId, ResourceId, Option<crate::svg_filter_graph::SvgFilterGraph>)>,
}
impl SvgFilterGraphBuilder {
pub fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn add_pass(
mut self,
input: ResourceId,
output: ResourceId,
graph: crate::svg_filter_graph::SvgFilterGraph,
) -> Self {
self.nodes.push((input, output, Some(graph)));
self
}
pub fn add_scene_pass(self, output: ResourceId, graph: crate::svg_filter_graph::SvgFilterGraph) -> Self {
self.add_pass(RES_SCENE, output, graph)
}
pub fn add_final_pass(self, input: ResourceId, graph: crate::svg_filter_graph::SvgFilterGraph) -> Self {
self.add_pass(input, RES_SCENE, graph)
}
pub fn build(self) -> Vec<SvgFilterNode> {
self.nodes
.into_iter()
.enumerate()
.map(|(i, (input, output, graph))| {
let mut node = SvgFilterNode::new(input, output);
if let Some(g) = graph {
node = node.with_filter_graph(g);
}
node.with_label(match i {
0 => "SvgFilter[0]",
1 => "SvgFilter[1]",
2 => "SvgFilter[2]",
_ => "SvgFilter[n]",
})
})
.collect()
}
}
impl Default for SvgFilterGraphBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod p1_35_filter_graph_tests {
use super::*;
#[test]
fn filter_node_identity() {
let node = SvgFilterNode::new(RES_SCENE, RES_FILTER_TEMP_A);
assert_eq!(node.inputs(), &[RES_SCENE]);
assert_eq!(node.outputs(), &[RES_FILTER_TEMP_A]);
assert_eq!(node.label(), "SvgFilter");
}
#[test]
fn filter_node_with_label() {
let node = SvgFilterNode::new(RES_SCENE, RES_FILTER_TEMP_A).with_label("MyFilter");
assert_eq!(node.label(), "MyFilter");
}
#[test]
fn filter_node_pass_id_is_post_process() {
let node = SvgFilterNode::new(RES_SCENE, RES_FILTER_TEMP_A);
match node.pass_id() {
crate::kvasir::nodes::PassId::PostProcess { pipeline_id } => {
assert_eq!(pipeline_id, 0xF1_000);
}
other => panic!("expected PostProcess, got {:?}", other),
}
}
#[test]
fn filter_graph_builder_creates_nodes() {
let builder = SvgFilterGraphBuilder::new();
let nodes = builder.build();
assert!(nodes.is_empty());
}
#[test]
fn filter_node_without_filter_graph_is_noop() {
let node = SvgFilterNode::new(RES_SCENE, RES_FILTER_TEMP_A);
assert!(node.filter_graph.is_none());
}
}