From e21757ce341dee0197aa11b2f528d5886679af91 Mon Sep 17 00:00:00 2001
From: StarArawn <toasterthegamer@gmail.com>
Date: Sat, 22 Jan 2022 10:34:33 -0500
Subject: [PATCH] Added properly SDF rendering of rounded rectangles. Added
 borders.

---
 .../src/render/unified/image/extract.rs       | 10 ++-
 .../src/render/unified/quad/extract.rs        | 64 +++++++++++++------
 bevy_kayak_ui/src/render/unified/shader.wgsl  | 59 ++++++++---------
 examples/image.rs                             |  5 ++
 kayak_core/src/node.rs                        | 44 +++++++++++--
 kayak_core/src/render_primitive.rs            |  4 ++
 kayak_core/src/styles.rs                      | 60 +++++++++--------
 src/widgets/window.rs                         |  6 +-
 8 files changed, 165 insertions(+), 87 deletions(-)

diff --git a/bevy_kayak_ui/src/render/unified/image/extract.rs b/bevy_kayak_ui/src/render/unified/image/extract.rs
index 04cfce8..c6b6e21 100644
--- a/bevy_kayak_ui/src/render/unified/image/extract.rs
+++ b/bevy_kayak_ui/src/render/unified/image/extract.rs
@@ -11,8 +11,12 @@ pub fn extract_images(
     image_manager: &Res<ImageManager>,
     dpi: f32,
 ) -> Vec<ExtractQuadBundle> {
-    let (layout, handle) = match render_command {
-        RenderPrimitive::Image { layout, handle } => (layout, handle),
+    let (border_radius, layout, handle) = match render_command {
+        RenderPrimitive::Image {
+            border_radius,
+            layout,
+            handle,
+        } => (*border_radius, layout, handle),
         _ => panic!(""),
     };
 
@@ -29,7 +33,7 @@ pub fn extract_images(
             font_handle: None,
             quad_type: UIQuadType::Image,
             type_index: 0,
-            border_radius: (0.0, 0.0, 0.0, 0.0),
+            border_radius,
             image: image_manager
                 .get_handle(handle)
                 .and_then(|a| Some(a.clone_weak())),
diff --git a/bevy_kayak_ui/src/render/unified/quad/extract.rs b/bevy_kayak_ui/src/render/unified/quad/extract.rs
index deb1acc..fa33c3e 100644
--- a/bevy_kayak_ui/src/render/unified/quad/extract.rs
+++ b/bevy_kayak_ui/src/render/unified/quad/extract.rs
@@ -7,32 +7,58 @@ use crate::{
 };
 
 pub fn extract_quads(render_primitive: &RenderPrimitive, dpi: f32) -> Vec<ExtractQuadBundle> {
-    let (background_color, layout, border_radius) = match render_primitive {
+    let (background_color, layout, border_radius, border) = match render_primitive {
         RenderPrimitive::Quad {
             background_color,
             layout,
             border_radius,
-        } => (background_color, layout, border_radius),
+            border,
+        } => (background_color, layout, border_radius, border),
         _ => panic!(""),
     };
 
-    vec![ExtractQuadBundle {
-        extracted_quad: ExtractedQuad {
-            rect: Rect {
-                min: Vec2::new(layout.posx, layout.posy),
-                max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height) * dpi,
+    vec![
+        // Border
+        ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(layout.posx, layout.posy),
+                    max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height) * dpi,
+                },
+                color: bevy::prelude::Color::rgba(0.0781, 0.0898, 0.101, 1.0),
+                vertex_index: 0,
+                char_id: 0,
+                z_index: layout.z_index,
+                font_handle: None,
+                quad_type: UIQuadType::Quad,
+                type_index: 0,
+                border_radius: *border_radius,
+                image: None,
+                uv_max: None,
+                uv_min: None,
             },
-            color: to_bevy_color(background_color),
-            vertex_index: 0,
-            char_id: 0,
-            z_index: layout.z_index,
-            font_handle: None,
-            quad_type: UIQuadType::Quad,
-            type_index: 0,
-            border_radius: *border_radius,
-            image: None,
-            uv_max: None,
-            uv_min: None,
         },
-    }]
+        ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(layout.posx + border.3, layout.posy + border.0),
+                    max: Vec2::new(
+                        (layout.posx + layout.width) - border.1,
+                        (layout.posy + layout.height) - border.2,
+                    ) * dpi,
+                },
+                color: to_bevy_color(background_color),
+                vertex_index: 0,
+                char_id: 0,
+                z_index: layout.z_index,
+                font_handle: None,
+                quad_type: UIQuadType::Quad,
+                type_index: 0,
+                border_radius: *border_radius,
+                image: None,
+                uv_max: None,
+                uv_min: None,
+            },
+        },
+    ]
 }
diff --git a/bevy_kayak_ui/src/render/unified/shader.wgsl b/bevy_kayak_ui/src/render/unified/shader.wgsl
index 1eb16eb..6195f7a 100644
--- a/bevy_kayak_ui/src/render/unified/shader.wgsl
+++ b/bevy_kayak_ui/src/render/unified/shader.wgsl
@@ -17,8 +17,8 @@ struct VertexOutput {
     [[location(1)]] uv: vec3<f32>;
     [[location(2)]] pos: vec2<f32>;
     [[location(3)]] size: vec2<f32>;
-    [[location(4)]] screen_position: vec2<f32>;
-    [[location(5)]] border_radius: f32;
+    [[location(4)]] border_radius: f32;
+    [[location(5)]] pixel_position: vec2<f32>;
 };
 
 [[stage(vertex)]]
@@ -30,9 +30,9 @@ fn vertex(
 ) -> VertexOutput {
     var out: VertexOutput;
     out.color = vertex_color;
-    out.pos = vertex_pos_size.xy;
+    out.pos = (vertex_position.xy - vertex_pos_size.xy);
     out.position = view.view_proj * vec4<f32>(vertex_position, 1.0);
-    out.screen_position = (view.view_proj * vec4<f32>(vertex_position, 1.0)).xy;
+    out.pixel_position = out.position.xy;
     out.uv = vertex_uv.xyz;
     out.size = vertex_pos_size.zw;
     out.border_radius = vertex_uv.w;
@@ -51,39 +51,26 @@ var image_sampler: sampler;
 
 let RADIUS: f32 = 0.1;
 
-fn sd_box_rounded(
-    frag_coord: vec2<f32>,
-    position: vec2<f32>,
-    size: vec2<f32>,
-    radius: f32,
-) -> f32 {
-    var inner_size: vec2<f32> = size - radius * 2.0;
-    var top_left: vec2<f32> = position + radius;
-    var bottom_right: vec2<f32> = top_left + inner_size;
-
-    var top_left_distance: vec2<f32> = top_left - frag_coord;
-    var bottom_right_distance: vec2<f32> = frag_coord - bottom_right;
-
-    var dist = max(max(top_left_distance, bottom_right_distance), vec2<f32>(0.0));
-
-    return length(dist);
+fn sdRoundBox(p: vec2<f32>, b: vec2<f32>, r: f32) -> f32 
+{
+    var q = abs(p)-b+r;
+    return min(max(q.x, q.y), 0.0) + length(max(q, vec2<f32>(0.0))) - r;
 }
 
 [[stage(fragment)]]
 fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
     if (quad_type.t == 0) {
-        var dist = sd_box_rounded(
-            in.position.xy,
-            in.pos,
-            in.size,
-            in.border_radius,
+        var border = 10.0;
+        var size = in.size;
+        var pos = in.pos.xy * 2.0;
+        var bs = min(in.border_radius * 2.0, min(in.size.x, in.size.y));
+        var rect_dist = sdRoundBox(
+            pos - size,
+            size,
+            bs,
         );
-        dist = 1.0 - smoothStep(
-            max(in.border_radius - 0.5, 0.0),
-            in.border_radius + 0.5,
-            dist);
-
-        return vec4<f32>(in.color.rgb, dist);
+        rect_dist = 1.0 - smoothStep(0.0, fwidth(rect_dist), rect_dist);
+        return vec4<f32>(in.color.rgb, rect_dist);
     }
     if (quad_type.t == 1) {
         var px_range = 3.5;
@@ -93,12 +80,18 @@ fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
         var v = max(min(x.r, x.g), min(max(x.r, x.g), x.b));
         var sig_dist = (v - 0.5) * dot(msdf_unit, 0.5 / fwidth(in.uv.xy));
         var a = clamp(sig_dist + 0.5, 0.0, 1.0);
-
         return vec4<f32>(in.color.rgb, a);
     }
     if (quad_type.t == 2) {
+        var bs = min(in.border_radius, min(in.size.x, in.size.y));
+        var mask = sdRoundBox(
+            in.pos.xy * 2.0 - (in.size.xy),
+            in.size.xy,
+            bs,
+        );
+        mask = 1.0 - smoothStep(0.0, fwidth(mask), mask);
         var color = textureSample(image_texture, image_sampler, vec2<f32>(in.uv.x, 1.0 - in.uv.y));
-        return vec4<f32>(color.rgb * in.color.rgb, color.a * in.color.a);
+        return vec4<f32>(color.rgb * in.color.rgb, color.a * in.color.a * mask);
     }
     return in.color;
 }
\ No newline at end of file
diff --git a/examples/image.rs b/examples/image.rs
index 9ec14ed..ec63e30 100644
--- a/examples/image.rs
+++ b/examples/image.rs
@@ -3,6 +3,7 @@ use bevy::{
     window::WindowDescriptor,
     DefaultPlugins,
 };
+use kayak_core::styles::PositionType;
 use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
 use kayak_ui::core::{
     render,
@@ -23,6 +24,10 @@ fn startup(
 
     let context = BevyContext::new(|context| {
         let image_styles = Style {
+            position_type: StyleProp::Value(PositionType::SelfDirected),
+            left: StyleProp::Value(Units::Pixels(10.0)),
+            top: StyleProp::Value(Units::Pixels(10.0)),
+            border_radius: StyleProp::Value((500.0, 500.0, 500.0, 500.0)),
             width: StyleProp::Value(Units::Pixels(200.0)),
             height: StyleProp::Value(Units::Pixels(182.0)),
             ..Style::default()
diff --git a/kayak_core/src/node.rs b/kayak_core/src/node.rs
index 09ca564..22b92ed 100644
--- a/kayak_core/src/node.rs
+++ b/kayak_core/src/node.rs
@@ -336,19 +336,55 @@ impl<'a> morphorm::Node<'a> for Index {
         Some(1)
     }
 
-    fn border_left(&self, _store: &'_ Self::Data) -> Option<morphorm::Units> {
+    fn border_left(&self, store: &'_ Self::Data) -> Option<morphorm::Units> {
+        if let Some(node) = store.get(*self) {
+            if let Some(node) = node {
+                return match node.styles.border {
+                    StyleProp::Default => Some(morphorm::Units::Auto),
+                    StyleProp::Value(prop) => Some(morphorm::Units::Pixels(prop.3)),
+                    _ => Some(morphorm::Units::Auto),
+                };
+            }
+        }
         Some(morphorm::Units::Auto)
     }
 
-    fn border_right(&self, _store: &'_ Self::Data) -> Option<morphorm::Units> {
+    fn border_right(&self, store: &'_ Self::Data) -> Option<morphorm::Units> {
+        if let Some(node) = store.get(*self) {
+            if let Some(node) = node {
+                return match node.styles.border {
+                    StyleProp::Default => Some(morphorm::Units::Auto),
+                    StyleProp::Value(prop) => Some(morphorm::Units::Pixels(prop.1)),
+                    _ => Some(morphorm::Units::Auto),
+                };
+            }
+        }
         Some(morphorm::Units::Auto)
     }
 
-    fn border_top(&self, _store: &'_ Self::Data) -> Option<morphorm::Units> {
+    fn border_top(&self, store: &'_ Self::Data) -> Option<morphorm::Units> {
+        if let Some(node) = store.get(*self) {
+            if let Some(node) = node {
+                return match node.styles.border {
+                    StyleProp::Default => Some(morphorm::Units::Auto),
+                    StyleProp::Value(prop) => Some(morphorm::Units::Pixels(prop.0)),
+                    _ => Some(morphorm::Units::Auto),
+                };
+            }
+        }
         Some(morphorm::Units::Auto)
     }
 
-    fn border_bottom(&self, _store: &'_ Self::Data) -> Option<morphorm::Units> {
+    fn border_bottom(&self, store: &'_ Self::Data) -> Option<morphorm::Units> {
+        if let Some(node) = store.get(*self) {
+            if let Some(node) = node {
+                return match node.styles.border {
+                    StyleProp::Default => Some(morphorm::Units::Auto),
+                    StyleProp::Value(prop) => Some(morphorm::Units::Pixels(prop.2)),
+                    _ => Some(morphorm::Units::Auto),
+                };
+            }
+        }
         Some(morphorm::Units::Auto)
     }
 }
diff --git a/kayak_core/src/render_primitive.rs b/kayak_core/src/render_primitive.rs
index 23acaef..3698b22 100644
--- a/kayak_core/src/render_primitive.rs
+++ b/kayak_core/src/render_primitive.rs
@@ -14,6 +14,7 @@ pub enum RenderPrimitive {
     Quad {
         layout: Rect,
         background_color: Color,
+        border: (f32, f32, f32, f32),
         border_radius: (f32, f32, f32, f32),
     },
     Text {
@@ -26,6 +27,7 @@ pub enum RenderPrimitive {
         size: f32,
     },
     Image {
+        border_radius: (f32, f32, f32, f32),
         layout: Rect,
         handle: u16,
     },
@@ -68,6 +70,7 @@ impl From<&Style> for RenderPrimitive {
             RenderCommand::Quad => Self::Quad {
                 background_color: background_color,
                 border_radius: style.border_radius.resolve(),
+                border: style.border.resolve(),
                 layout: Rect::default(),
             },
             RenderCommand::Text {
@@ -86,6 +89,7 @@ impl From<&Style> for RenderPrimitive {
                 size,
             },
             RenderCommand::Image { handle } => Self::Image {
+                border_radius: style.border_radius.resolve(),
                 layout: Rect::default(),
                 handle,
             },
diff --git a/kayak_core/src/styles.rs b/kayak_core/src/styles.rs
index 38b5116..14d014b 100644
--- a/kayak_core/src/styles.rs
+++ b/kayak_core/src/styles.rs
@@ -36,29 +36,30 @@ where
 pub struct Style {
     pub background_color: StyleProp<Color>,
     pub border_radius: StyleProp<(f32, f32, f32, f32)>,
+    pub border: StyleProp<(f32, f32, f32, f32)>,
     pub bottom: StyleProp<Units>,
     pub color: StyleProp<Color>,
     pub height: StyleProp<Units>,
     pub layout_type: StyleProp<LayoutType>,
     pub left: StyleProp<Units>,
-    pub position_type: StyleProp<PositionType>,
-    pub render_command: StyleProp<RenderCommand>,
-    pub right: StyleProp<Units>,
-    pub top: StyleProp<Units>,
-    pub width: StyleProp<Units>,
-    pub padding_left: StyleProp<Units>,
-    pub padding_right: StyleProp<Units>,
-    pub padding_top: StyleProp<Units>,
-    pub padding_bottom: StyleProp<Units>,
+    pub margin_bottom: StyleProp<Units>,
     pub margin_left: StyleProp<Units>,
     pub margin_right: StyleProp<Units>,
     pub margin_top: StyleProp<Units>,
-    pub margin_bottom: StyleProp<Units>,
-    pub min_width: StyleProp<Units>,
-    pub min_height: StyleProp<Units>,
-    pub max_width: StyleProp<Units>,
     pub max_height: StyleProp<Units>,
+    pub max_width: StyleProp<Units>,
+    pub min_height: StyleProp<Units>,
+    pub min_width: StyleProp<Units>,
+    pub padding_bottom: StyleProp<Units>,
+    pub padding_left: StyleProp<Units>,
+    pub padding_right: StyleProp<Units>,
+    pub padding_top: StyleProp<Units>,
     pub pointer_events: StyleProp<PointerEvents>,
+    pub position_type: StyleProp<PositionType>,
+    pub render_command: StyleProp<RenderCommand>,
+    pub right: StyleProp<Units>,
+    pub top: StyleProp<Units>,
+    pub width: StyleProp<Units>,
 }
 
 impl Default for Style {
@@ -66,29 +67,30 @@ impl Default for Style {
         Self {
             background_color: StyleProp::Default,
             border_radius: StyleProp::Default,
-            render_command: StyleProp::Value(RenderCommand::Empty),
+            border: StyleProp::Default,
             bottom: StyleProp::Default,
             color: StyleProp::Inherit,
             height: StyleProp::Default,
             layout_type: StyleProp::Default,
             left: StyleProp::Default,
-            position_type: StyleProp::Default,
-            right: StyleProp::Default,
-            top: StyleProp::Default,
-            width: StyleProp::Default,
-            padding_left: StyleProp::Default,
-            padding_right: StyleProp::Default,
-            padding_top: StyleProp::Default,
-            padding_bottom: StyleProp::Default,
+            margin_bottom: StyleProp::Default,
             margin_left: StyleProp::Default,
             margin_right: StyleProp::Default,
             margin_top: StyleProp::Default,
-            margin_bottom: StyleProp::Default,
-            min_width: StyleProp::Default,
-            min_height: StyleProp::Default,
-            max_width: StyleProp::Default,
             max_height: StyleProp::Default,
+            max_width: StyleProp::Default,
+            min_height: StyleProp::Default,
+            min_width: StyleProp::Default,
+            padding_bottom: StyleProp::Default,
+            padding_left: StyleProp::Default,
+            padding_right: StyleProp::Default,
+            padding_top: StyleProp::Default,
             pointer_events: StyleProp::Default,
+            position_type: StyleProp::Default,
+            render_command: StyleProp::Value(RenderCommand::Empty),
+            right: StyleProp::Default,
+            top: StyleProp::Default,
+            width: StyleProp::Default,
         }
     }
 }
@@ -107,6 +109,12 @@ impl Style {
             }
             _ => (),
         }
+        match self.border {
+            StyleProp::Inherit => {
+                self.border = other.border.clone();
+            }
+            _ => (),
+        }
         match self.bottom {
             StyleProp::Inherit => {
                 self.bottom = other.bottom.clone();
diff --git a/src/widgets/window.rs b/src/widgets/window.rs
index c866c6a..a362b50 100644
--- a/src/widgets/window.rs
+++ b/src/widgets/window.rs
@@ -18,6 +18,7 @@ pub fn Window(
 ) {
     *styles = Some(Style {
         background_color: StyleProp::Value(Color::new(0.125, 0.125, 0.125, 1.0)),
+        border: StyleProp::Value((4.0, 4.0, 4.0, 4.0)),
         border_radius: StyleProp::Value((5.0, 5.0, 5.0, 5.0)),
         render_command: StyleProp::Value(RenderCommand::Quad),
         position_type: StyleProp::Value(PositionType::SelfDirected),
@@ -35,8 +36,8 @@ pub fn Window(
         padding_right: StyleProp::Value(Units::Pixels(5.0)),
         padding_top: StyleProp::Value(Units::Pixels(5.0)),
         padding_bottom: StyleProp::Value(Units::Pixels(5.0)),
-        width: StyleProp::Value(Units::Pixels(size.0)),
-        height: StyleProp::Value(Units::Pixels(size.1)),
+        width: StyleProp::Value(Units::Stretch(1.0)),
+        height: StyleProp::Value(Units::Stretch(1.0)),
         max_width: StyleProp::Value(Units::Pixels(size.0)),
         max_height: StyleProp::Value(Units::Pixels(size.1)),
         ..Style::default()
@@ -46,6 +47,7 @@ pub fn Window(
         background_color: StyleProp::Value(Color::new(0.0781, 0.0898, 0.101, 1.0)),
         border_radius: StyleProp::Value((5.0, 0.0, 0.0, 5.0)),
         height: StyleProp::Value(Units::Pixels(24.0)),
+        width: StyleProp::Value(Units::Stretch(1.0)),
         left: StyleProp::Value(Units::Pixels(0.0)),
         right: StyleProp::Value(Units::Pixels(0.0)),
         top: StyleProp::Value(Units::Pixels(0.0)),
-- 
GitLab