Improve previewing node data (#1446)

* Improve preview

* Improve contrast

* Restructure in order to duplicate code

* Code review nits

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube 2023-11-04 09:52:26 +00:00 committed by GitHub
parent c823016316
commit e0ac073805
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 326 additions and 202 deletions

View file

@ -204,6 +204,18 @@ impl<'a, T> AsRef<EditorApi<'a, T>> for EditorApi<'a, T> {
}
}
// Required for the EndLetNode
impl<'a, IO> From<EditorApi<'a, IO>> for Footprint {
fn from(value: EditorApi<'a, IO>) -> Self {
value.render_config.viewport
}
}
// Required for the EndLetNode
impl<'a, IO> From<EditorApi<'a, IO>> for () {
fn from(_value: EditorApi<'a, IO>) -> Self {}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ExtractImageFrame;

View file

@ -88,6 +88,15 @@ impl SvgRender {
self.svg.push("</svg>");
}
/// Wraps the SVG with `<svg><g transform="...">`, which allows for rotation
pub fn wrap_with_transform(&mut self, transform: DAffine2) {
let defs = &self.svg_defs;
let svg_header = format!(r#"<svg xmlns="http://www.w3.org/2000/svg"><defs>{defs}</defs><g transform="{}">"#, format_transform_matrix(transform));
self.svg.insert(0, svg_header.into());
self.svg.push("</g></svg>");
}
pub fn leaf_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs)) {
self.indent();
self.svg.push("<");
@ -97,6 +106,11 @@ impl SvgRender {
self.svg.push("/>");
}
pub fn leaf_node(&mut self, content: impl Into<SvgSegment>) {
self.indent();
self.svg.push(content);
}
pub fn parent_tag(&mut self, name: impl Into<SvgSegment>, attributes: impl FnOnce(&mut SvgRenderAttrs), inner: impl FnOnce(&mut Self)) {
let name = name.into();
self.indent();
@ -359,6 +373,55 @@ impl GraphicElementRendered for GraphicElementData {
}
}
/// Used to stop rust complaining about upstream traits adding display implementations to `Option<Color>`. This would not be an issue as we control that crate.
trait Primitive: core::fmt::Display {}
impl Primitive for String {}
impl Primitive for bool {}
impl Primitive for f32 {}
impl Primitive for f64 {}
fn text_attributes(attributes: &mut SvgRenderAttrs) {
attributes.push("fill", "white");
attributes.push("y", "30");
attributes.push("font-size", "30");
}
impl<T: Primitive> GraphicElementRendered for T {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
render.parent_tag("text", text_attributes, |render| render.leaf_node(format!("{self}")));
}
fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> {
None
}
fn add_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
}
impl GraphicElementRendered for Option<Color> {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
let Some(color) = self else {
render.parent_tag("text", |_| {}, |render| render.leaf_node("Empty color"));
return;
};
render.leaf_tag("rect", |attributes| {
attributes.push("width", "100");
attributes.push("height", "100");
attributes.push("y", "40");
attributes.push("fill", format!("#{}", color.rgba_hex()));
});
let color_info = format!("{:?} #{} {:?}", color, color.rgba_hex(), color.to_rgba8_srgb());
render.parent_tag("text", text_attributes, |render| render.leaf_node(color_info))
}
fn bounding_box(&self, _transform: DAffine2) -> Option<[DVec2; 2]> {
None
}
fn add_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
}
/// A segment of an svg string to allow for embedding blob urls
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SvgSegment {

View file

@ -111,23 +111,24 @@ impl<T> LetNode<T> {
/// Caches the output of a given Node and acts as a proxy
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EndLetNode<Input> {
pub struct EndLetNode<Input, Parameter> {
input: Input,
paramenter: PhantomData<Parameter>,
}
impl<'i, T: 'i, Input> Node<'i, T> for EndLetNode<Input>
impl<'i, T: 'i, Parameter: 'i + From<T>, Input> Node<'i, T> for EndLetNode<Input, Parameter>
where
Input: Node<'i, ()>,
Input: Node<'i, Parameter>,
{
type Output = <Input>::Output;
fn eval(&'i self, _: T) -> Self::Output {
let result = self.input.eval(());
fn eval(&'i self, t: T) -> Self::Output {
let result = self.input.eval(Parameter::from(t));
result
}
}
impl<Input> EndLetNode<Input> {
pub const fn new(input: Input) -> EndLetNode<Input> {
EndLetNode { input }
impl<Input, Parameter> EndLetNode<Input, Parameter> {
pub const fn new(input: Input) -> EndLetNode<Input, Parameter> {
EndLetNode { input, paramenter: PhantomData }
}
}

View file

@ -65,6 +65,8 @@ pub struct GenerateBrightnessContrastMapperNode<Brightness, Contrast> {
contrast: Contrast,
}
// TODO: Replace this node implementation with one that reuses the more generalized Curves adjustment node.
// TODO: It will be necessary to ensure the tests below are faithfully translated in a way that ensures identical results.
#[node_macro::node_fn(GenerateBrightnessContrastMapperNode)]
fn brightness_contrast_node(_primary: (), brightness: f32, contrast: f32) -> BrightnessContrastMapperNode {
// Brightness LUT

View file

@ -127,12 +127,12 @@ impl Gradient {
// Compute the color of the inserted stop
let get_color = |index: usize, time: f64| match (self.positions[index].1, self.positions.get(index + 1).and_then(|x| x.1)) {
// Lerp between the nearest colours if applicable
// Lerp between the nearest colors if applicable
(Some(a), Some(b)) => a.lerp(
b,
((time - self.positions[index].0) / self.positions.get(index + 1).map(|end| end.0 - self.positions[index].0).unwrap_or_default()) as f32,
),
// Use the start or the end colour if applicable
// Use the start or the end color if applicable
(Some(v), _) | (_, Some(v)) => v,
_ => Color::WHITE,
};