diff options
Diffstat (limited to '')
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | examples/pure/arc/Cargo.toml | 9 | ||||
| -rw-r--r-- | examples/pure/arc/README.md | 14 | ||||
| -rw-r--r-- | examples/pure/arc/src/main.rs | 124 | ||||
| -rw-r--r-- | graphics/src/widget/canvas/path/builder.rs | 53 | ||||
| -rw-r--r-- | style/src/theme/palette.rs | 10 | 
6 files changed, 199 insertions, 12 deletions
@@ -90,6 +90,7 @@ members = [      "examples/tour",      "examples/url_handler",      "examples/websocket", +    "examples/pure/arc",      "examples/pure/component",      "examples/pure/counter",      "examples/pure/game_of_life", diff --git a/examples/pure/arc/Cargo.toml b/examples/pure/arc/Cargo.toml new file mode 100644 index 00000000..22113cf1 --- /dev/null +++ b/examples/pure/arc/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "arc" +version = "0.1.0" +authors = ["ThatsNoMoon <git@thatsnomoon.dev>"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../../..", features = ["pure", "canvas", "tokio", "debug"] } diff --git a/examples/pure/arc/README.md b/examples/pure/arc/README.md new file mode 100644 index 00000000..303253da --- /dev/null +++ b/examples/pure/arc/README.md @@ -0,0 +1,14 @@ +## Arc + +An application that uses the `Canvas` widget to draw a rotating arc. + +This is a simple demo for https://github.com/iced-rs/iced/pull/1358. + +The __[`main`]__ file contains all the code of the example. + +You can run it with `cargo run`: +``` +cargo run --package arc +``` + +[`main`]: src/main.rs diff --git a/examples/pure/arc/src/main.rs b/examples/pure/arc/src/main.rs new file mode 100644 index 00000000..df0e1e8a --- /dev/null +++ b/examples/pure/arc/src/main.rs @@ -0,0 +1,124 @@ +use std::{f32::consts::PI, time::Instant}; + +use iced::executor; +use iced::pure::widget::canvas::{ +    self, Cache, Canvas, Cursor, Geometry, Path, Stroke, +}; +use iced::pure::{Application, Element}; +use iced::{Command, Length, Point, Rectangle, Settings, Subscription, Theme}; + +pub fn main() -> iced::Result { +    Arc::run(Settings { +        antialiasing: true, +        ..Settings::default() +    }) +} + +struct Arc { +    start: Instant, +    cache: Cache, +} + +#[derive(Debug, Clone, Copy)] +enum Message { +    Tick, +} + +impl Application for Arc { +    type Executor = executor::Default; +    type Message = Message; +    type Theme = Theme; +    type Flags = (); + +    fn new(_flags: ()) -> (Self, Command<Message>) { +        ( +            Arc { +                start: Instant::now(), +                cache: Default::default(), +            }, +            Command::none(), +        ) +    } + +    fn title(&self) -> String { +        String::from("Arc - Iced") +    } + +    fn update(&mut self, _: Message) -> Command<Message> { +        self.cache.clear(); + +        Command::none() +    } + +    fn subscription(&self) -> Subscription<Message> { +        iced::time::every(std::time::Duration::from_millis(10)) +            .map(|_| Message::Tick) +    } + +    fn view(&self) -> Element<Message> { +        Canvas::new(self) +            .width(Length::Fill) +            .height(Length::Fill) +            .into() +    } + +    fn theme(&self) -> Theme { +        Theme::Dark +    } +} + +impl<Message> canvas::Program<Message> for Arc { +    type State = (); + +    fn draw( +        &self, +        _state: &Self::State, +        theme: &Theme, +        bounds: Rectangle, +        _cursor: Cursor, +    ) -> Vec<Geometry> { +        let geometry = self.cache.draw(bounds.size(), |frame| { +            let palette = theme.palette(); + +            let center = frame.center(); +            let radius = frame.width().min(frame.height()) / 5.0; + +            let start = Point::new(center.x, center.y - radius); + +            let angle = (self.start.elapsed().as_millis() % 10_000) as f32 +                / 10_000.0 +                * 2.0 +                * PI; + +            let end = Point::new( +                center.x + radius * angle.cos(), +                center.y + radius * angle.sin(), +            ); + +            let circles = Path::new(|b| { +                b.circle(start, 10.0); +                b.move_to(end); +                b.circle(end, 10.0); +            }); + +            frame.fill(&circles, palette.text); + +            let path = Path::new(|b| { +                b.move_to(start); +                b.arc_to(center, end, 50.0); +                b.line_to(end); +            }); + +            frame.stroke( +                &path, +                Stroke { +                    color: palette.text, +                    width: 10.0, +                    ..Stroke::default() +                }, +            ); +        }); + +        vec![geometry] +    } +} diff --git a/graphics/src/widget/canvas/path/builder.rs b/graphics/src/widget/canvas/path/builder.rs index 05316d8a..88acf544 100644 --- a/graphics/src/widget/canvas/path/builder.rs +++ b/graphics/src/widget/canvas/path/builder.rs @@ -42,22 +42,61 @@ impl Builder {      /// Adds a circular arc to the [`Path`] with the given control points and      /// radius.      /// -    /// The arc is connected to the previous point by a straight line, if -    /// necessary. +    /// This essentially draws a straight line segment from the current +    /// position to `a`, but fits a circular arc of `radius` tangent to that +    /// segment and tangent to the line between `a` and `b`. +    /// +    /// With another `.line_to(b)`, the result will be a path connecting the +    /// starting point and `b` with straight line segments towards `a` and a +    /// circular arc smoothing out the corner at `a`. +    /// +    /// See [the HTML5 specification of `arcTo`](https://html.spec.whatwg.org/multipage/canvas.html#building-paths:dom-context-2d-arcto) +    /// for more details and examples.      pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) {          use lyon::{math, path}; -        let a = math::Point::new(a.x, a.y); +        let start = self.raw.current_position(); +        let mid = math::Point::new(a.x, a.y); +        let end = math::Point::new(b.x, b.y); + +        if start == mid || mid == end || radius == 0.0 { +            let _ = self.raw.line_to(mid); +            return; +        } + +        let double_area = start.x * (mid.y - end.y) +            + mid.x * (end.y - start.y) +            + end.x * (start.y - mid.y); -        if self.raw.current_position() != a { -            let _ = self.raw.line_to(a); +        if double_area == 0.0 { +            let _ = self.raw.line_to(mid); +            return;          } +        let to_start = (start - mid).normalize(); +        let to_end = (end - mid).normalize(); + +        let inner_angle = to_start.dot(to_end).acos(); + +        let origin_angle = inner_angle / 2.0; + +        let origin_adjacent = radius / origin_angle.tan(); + +        let arc_start = mid + to_start * origin_adjacent; +        let arc_end = mid + to_end * origin_adjacent; + +        let sweep = to_start.cross(to_end) < 0.0; + +        let _ = self.raw.line_to(arc_start); +          self.raw.arc_to(              math::Vector::new(radius, radius),              math::Angle::radians(0.0), -            path::ArcFlags::default(), -            math::Point::new(b.x, b.y), +            path::ArcFlags { +                large_arc: false, +                sweep, +            }, +            arc_end,          );      } diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index cb8bb6e6..81aa9cc7 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -5,11 +5,11 @@ use palette::{FromColor, Hsl, Mix, RelativeContrast, Srgb};  #[derive(Debug, Clone, Copy, PartialEq)]  pub struct Palette { -    background: Color, -    text: Color, -    primary: Color, -    success: Color, -    danger: Color, +    pub background: Color, +    pub text: Color, +    pub primary: Color, +    pub success: Color, +    pub danger: Color,  }  impl Palette {  | 
