use iced::Color;
use iced_nodegraph_sdf::{Pattern, Stop, Style};
use crate::node_pin::PinDirection;
use super::{EdgeStyle, NodeStyle, PinStyle};
fn transparent(c: Color) -> Color {
Color { a: 0.0, ..c }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum EdgeGeometry {
Stroke,
Shadow,
}
#[derive(Debug, Clone)]
pub(crate) struct EdgeLayer {
pub geometry: EdgeGeometry,
pub style: Style,
}
impl NodeStyle {
pub(crate) fn fill_sdf_style(&self, opacity: f32) -> Style {
Style::quad_band(&self.fill_color.with_opacity(opacity), -1e6, 0.0)
}
pub(crate) fn border_sdf_layers(&self, opacity: f32) -> Vec<Style> {
let bw = self.border_pattern.thickness;
if bw <= 0.0 {
return Vec::new();
}
let has_outline = self.border_outline_width > 0.0;
if !self.border_pattern.is_solid() {
let mut layers = vec![Style::quad_stroke(
&self.border_color.with_opacity(opacity),
self.border_pattern,
)];
if has_outline {
let ow = self.border_outline_width;
layers.push(Style::quad_band(
&self.border_outline_color.with_opacity(opacity),
0.0,
bw + ow * 2.0,
));
}
return layers;
}
let (bs, be) = self.border_color.with_opacity(opacity).arc_pair();
let mut stops = vec![
Stop::grad(0.0, transparent(bs), transparent(be)),
Stop::grad(0.0, bs, be),
];
if has_outline {
let outer = bw + self.border_outline_width * 2.0;
let (os, oe) = self.border_outline_color.with_opacity(opacity).arc_pair();
stops.push(Stop::grad(bw, bs, be));
stops.push(Stop::grad(bw, os, oe));
stops.push(Stop::grad(outer, os, oe));
stops.push(Stop::grad(outer, transparent(os), transparent(oe)));
} else {
stops.push(Stop::grad(bw, bs, be));
stops.push(Stop::grad(bw, transparent(bs), transparent(be)));
}
vec![Style {
stops,
pattern: None,
transfer: Default::default(),
}]
}
pub(crate) fn has_shadow(&self) -> bool {
self.shadow_distance > 0.0 && self.shadow_color.a > 0.0
}
pub(crate) fn shadow_sdf_layers(&self, opacity: f32) -> Vec<Style> {
let d = self.shadow_distance.max(0.001);
let full = Color {
a: self.shadow_color.a * opacity,
..self.shadow_color
};
let none = transparent(full);
vec![Style {
stops: vec![Stop::new(-d, full), Stop::new(d, none)],
pattern: None,
transfer: Default::default(),
}]
}
}
impl EdgeStyle {
pub(crate) fn sdf_layers(&self) -> Vec<EdgeLayer> {
let mut layers = Vec::with_capacity(6);
layers.push(EdgeLayer {
geometry: EdgeGeometry::Stroke,
style: Style::quad_stroke(&self.stroke_color, self.pattern),
});
if self.stroke_outline_width > 0.0
&& (self.stroke_outline_color.near_start.a > 0.0
|| self.stroke_outline_color.near_end.a > 0.0)
{
let outline_pat =
Pattern::solid(self.pattern.thickness + self.stroke_outline_width * 2.0);
layers.push(EdgeLayer {
geometry: EdgeGeometry::Stroke,
style: Style::quad_stroke(&self.stroke_outline_color, outline_pat),
});
}
if self.border_width > 0.0 {
let border_center =
self.pattern.thickness * 0.5 + self.border_gap + self.border_width * 0.5;
let border_outer = border_center + self.border_width * 0.5;
layers.push(EdgeLayer {
geometry: EdgeGeometry::Stroke,
style: Style::quad_stroke(&self.border_color, Pattern::solid(self.border_width)),
});
if self.border_outline_width > 0.0 {
let outline_pat =
Pattern::solid(self.border_width + self.border_outline_width * 2.0);
layers.push(EdgeLayer {
geometry: EdgeGeometry::Stroke,
style: Style::quad_stroke(&self.border_outline_color, outline_pat),
});
}
if self.border_background.near_start.a > 0.0 || self.border_background.near_end.a > 0.0
{
layers.push(EdgeLayer {
geometry: EdgeGeometry::Stroke,
style: Style::quad_band(&self.border_background, -1e6, border_outer),
});
}
}
if self.shadow_blur > 0.0
&& (self.shadow_color.near_start.a > 0.0 || self.shadow_color.near_end.a > 0.0)
{
layers.push(EdgeLayer {
geometry: EdgeGeometry::Shadow,
style: Style::quad_band(
&self.shadow_color,
-self.shadow_expand,
self.shadow_expand + self.shadow_blur.max(0.001),
),
});
}
layers
}
}
impl PinStyle {
pub(crate) fn sdf_layers(&self, direction: PinDirection, indicator_r: f32) -> Vec<Style> {
let mut layers = Vec::with_capacity(2);
let fill = if direction == PinDirection::Input {
Style::quad_stroke(&self.color, Pattern::solid(indicator_r * 0.8))
} else {
Style::quad_band(&self.color, -1e6, 0.0)
};
layers.push(fill);
if self.border_width > 0.0
&& (self.border_color.near_start.a > 0.0 || self.border_color.near_end.a > 0.0)
{
layers.push(Style::quad_band(&self.border_color, -1e6, 0.0).expand(self.border_width));
}
layers
}
}
#[cfg(test)]
mod shadow_tests {
use super::NodeStyle;
#[test]
fn shadow_fills_interior_and_fades_out() {
let style = NodeStyle::input();
let layers = style.shadow_sdf_layers(1.0);
assert_eq!(layers.len(), 1, "shadow must be a single entry");
let stops = &layers[0].stops;
assert_eq!(stops.len(), 2, "solid core -> transparent outside");
assert_eq!(
stops[0].dist, -style.shadow_distance,
"gradient starts at -shadow_distance (solid core held below it)",
);
assert_eq!(
stops[0].start.a, style.shadow_color.a,
"full at the inner edge of the gradient",
);
assert_eq!(
stops[1].dist, style.shadow_distance,
"fades out at +shadow_distance"
);
assert_eq!(stops[1].start.a, 0.0, "transparent at the outer edge");
}
}