From ede4440e997191c947697ee7aab89f32b9d0b2ea Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Tue, 2 Jun 2020 02:21:07 +0200
Subject: Introduce fill rule setting in `canvas`

---
 graphics/src/widget/canvas.rs       |  4 +--
 graphics/src/widget/canvas/fill.rs  | 50 +++++++++++++++++++++++++++++++++----
 graphics/src/widget/canvas/frame.rs | 27 ++++++++------------
 3 files changed, 57 insertions(+), 24 deletions(-)

(limited to 'graphics')

diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs
index d393a5c5..b8466239 100644
--- a/graphics/src/widget/canvas.rs
+++ b/graphics/src/widget/canvas.rs
@@ -29,7 +29,7 @@ mod text;
 pub use cache::Cache;
 pub use cursor::Cursor;
 pub use event::Event;
-pub use fill::Fill;
+pub use fill::{Fill, FillRule};
 pub use frame::Frame;
 pub use geometry::Geometry;
 pub use path::Path;
@@ -84,7 +84,7 @@ pub use text::Text;
 ///         let circle = Path::circle(frame.center(), self.radius);
 ///
 ///         // And fill it with some color
-///         frame.fill(&circle, Fill::Color(Color::BLACK));
+///         frame.fill(&circle, Color::BLACK);
 ///
 ///         // Finally, we produce the geometry
 ///         vec![frame.into_geometry()]
diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs
index a2010e45..56495435 100644
--- a/graphics/src/widget/canvas/fill.rs
+++ b/graphics/src/widget/canvas/fill.rs
@@ -2,19 +2,59 @@ use iced_native::Color;
 
 /// The style used to fill geometry.
 #[derive(Debug, Clone, Copy)]
-pub enum Fill {
-    /// Fill with a color.
-    Color(Color),
+pub struct Fill {
+    /// The color used to fill geometry.
+    ///
+    /// By default, it is set to `BLACK`.
+    pub color: Color,
+
+    /// The fill rule defines how to determine what is inside and what is
+    /// outside of a shape.
+    ///
+    /// See the [SVG specification][1] for more details.
+    ///
+    /// By default, it is set to `NonZero`.
+    ///
+    /// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
+    pub rule: FillRule,
 }
 
 impl Default for Fill {
     fn default() -> Fill {
-        Fill::Color(Color::BLACK)
+        Fill {
+            color: Color::BLACK,
+            rule: FillRule::NonZero,
+        }
     }
 }
 
 impl From<Color> for Fill {
     fn from(color: Color) -> Fill {
-        Fill::Color(color)
+        Fill {
+            color,
+            ..Fill::default()
+        }
+    }
+}
+
+/// The fill rule defines how to determine what is inside and what is outside of
+/// a shape.
+///
+/// See the [SVG specification][1].
+///
+/// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[allow(missing_docs)]
+pub enum FillRule {
+    NonZero,
+    EvenOdd,
+}
+
+impl From<FillRule> for lyon::tessellation::FillRule {
+    fn from(rule: FillRule) -> lyon::tessellation::FillRule {
+        match rule {
+            FillRule::NonZero => lyon::tessellation::FillRule::NonZero,
+            FillRule::EvenOdd => lyon::tessellation::FillRule::EvenOdd,
+        }
     }
 }
diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs
index 48d28d95..b5c6a2b1 100644
--- a/graphics/src/widget/canvas/frame.rs
+++ b/graphics/src/widget/canvas/frame.rs
@@ -92,29 +92,22 @@ impl Frame {
             BuffersBuilder, FillOptions, FillTessellator,
         };
 
+        let Fill { color, rule } = fill.into();
+
         let mut buffers = BuffersBuilder::new(
             &mut self.buffers,
-            FillVertex(match fill.into() {
-                Fill::Color(color) => color.into_linear(),
-            }),
+            FillVertex(color.into_linear()),
         );
 
         let mut tessellator = FillTessellator::new();
+        let options = FillOptions::default().with_fill_rule(rule.into());
 
         let result = if self.transforms.current.is_identity {
-            tessellator.tessellate_path(
-                path.raw(),
-                &FillOptions::default(),
-                &mut buffers,
-            )
+            tessellator.tessellate_path(path.raw(), &options, &mut buffers)
         } else {
             let path = path.transformed(&self.transforms.current.raw);
 
-            tessellator.tessellate_path(
-                path.raw(),
-                &FillOptions::default(),
-                &mut buffers,
-            )
+            tessellator.tessellate_path(path.raw(), &options, &mut buffers)
         };
 
         let _ = result.expect("Tessellate path");
@@ -132,11 +125,11 @@ impl Frame {
     ) {
         use lyon::tessellation::{BuffersBuilder, FillOptions};
 
+        let Fill { color, rule } = fill.into();
+
         let mut buffers = BuffersBuilder::new(
             &mut self.buffers,
-            FillVertex(match fill.into() {
-                Fill::Color(color) => color.into_linear(),
-            }),
+            FillVertex(color.into_linear()),
         );
 
         let top_left =
@@ -151,7 +144,7 @@ impl Frame {
 
         let _ = lyon::tessellation::basic_shapes::fill_rectangle(
             &lyon::math::Rect::new(top_left, size.into()),
-            &FillOptions::default(),
+            &FillOptions::default().with_fill_rule(rule.into()),
             &mut buffers,
         )
         .expect("Fill rectangle");
-- 
cgit