Adds linear gradient support to 2D meshes in the canvas widget.

This commit is contained in:
shan 2022-09-29 10:52:58 -07:00
parent 97f385e093
commit 00a8a16712
40 changed files with 2041 additions and 653 deletions

View file

@ -2,7 +2,7 @@ use std::{f32::consts::PI, time::Instant};
use iced::executor;
use iced::widget::canvas::{
self, Cache, Canvas, Cursor, Geometry, Path, Stroke,
self, Cache, Canvas, Cursor, Geometry, Path, Stroke, StrokeStyle,
};
use iced::{
Application, Command, Element, Length, Point, Rectangle, Settings,
@ -52,11 +52,6 @@ impl Application for Arc {
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)
@ -67,6 +62,11 @@ impl Application for Arc {
fn theme(&self) -> Theme {
Theme::Dark
}
fn subscription(&self) -> Subscription<Message> {
iced::time::every(std::time::Duration::from_millis(10))
.map(|_| Message::Tick)
}
}
impl<Message> canvas::Program<Message> for Arc {
@ -114,7 +114,7 @@ impl<Message> canvas::Program<Message> for Arc {
frame.stroke(
&path,
Stroke {
color: palette.text,
style: StrokeStyle::Solid(palette.text),
width: 10.0,
..Stroke::default()
},

View file

@ -1,5 +1,7 @@
use iced::executor;
use iced::widget::canvas::{Cache, Cursor, Geometry, LineCap, Path, Stroke};
use iced::widget::canvas::{
Cache, Cursor, Geometry, LineCap, Path, Stroke, StrokeStyle,
};
use iced::widget::{canvas, container};
use iced::{
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
@ -24,9 +26,9 @@ enum Message {
}
impl Application for Clock {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Executor = executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
@ -59,15 +61,6 @@ impl Application for Clock {
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
iced::time::every(std::time::Duration::from_millis(500)).map(|_| {
Message::Tick(
time::OffsetDateTime::now_local()
.unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
)
})
}
fn view(&self) -> Element<Message> {
let canvas = canvas(self as &Self)
.width(Length::Fill)
@ -79,6 +72,15 @@ impl Application for Clock {
.padding(20)
.into()
}
fn subscription(&self) -> Subscription<Message> {
iced::time::every(std::time::Duration::from_millis(500)).map(|_| {
Message::Tick(
time::OffsetDateTime::now_local()
.unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
)
})
}
}
impl<Message> canvas::Program<Message> for Clock {
@ -104,33 +106,41 @@ impl<Message> canvas::Program<Message> for Clock {
let long_hand =
Path::line(Point::ORIGIN, Point::new(0.0, -0.8 * radius));
let thin_stroke = Stroke {
width: radius / 100.0,
color: Color::WHITE,
line_cap: LineCap::Round,
..Stroke::default()
let width = radius / 100.0;
let thin_stroke = || -> Stroke {
Stroke {
width,
style: StrokeStyle::Solid(Color::WHITE),
line_cap: LineCap::Round,
..Stroke::default()
}
};
let wide_stroke = Stroke {
width: thin_stroke.width * 3.0,
..thin_stroke
let wide_stroke = || -> Stroke {
Stroke {
width: width * 3.0,
style: StrokeStyle::Solid(Color::WHITE),
line_cap: LineCap::Round,
..Stroke::default()
}
};
frame.translate(Vector::new(center.x, center.y));
frame.with_save(|frame| {
frame.rotate(hand_rotation(self.now.hour(), 12));
frame.stroke(&short_hand, wide_stroke);
frame.stroke(&short_hand, wide_stroke());
});
frame.with_save(|frame| {
frame.rotate(hand_rotation(self.now.minute(), 60));
frame.stroke(&long_hand, wide_stroke);
frame.stroke(&long_hand, wide_stroke());
});
frame.with_save(|frame| {
frame.rotate(hand_rotation(self.now.second(), 60));
frame.stroke(&long_hand, thin_stroke);
frame.stroke(&long_hand, thin_stroke());
})
});

View file

@ -1,6 +1,9 @@
//! This example showcases a simple native custom widget that renders using
//! arbitrary low-level geometry.
//!
//TODO need to update this now that vertex data doesn't contain color data
mod rainbow {
use iced::Color;
// For now, to implement a custom native widget you will need to add
// `iced_native` and `iced_wgpu` to your dependencies.
//
@ -12,6 +15,7 @@ mod rainbow {
// implemented by `iced_wgpu` and other renderers.
use iced_graphics::renderer::{self, Renderer};
use iced_graphics::{Backend, Primitive};
use iced_graphics::shader::Shader;
use iced_native::widget::{self, Widget};
use iced_native::{
@ -63,20 +67,20 @@ mod rainbow {
cursor_position: Point,
_viewport: &Rectangle,
) {
use iced_graphics::triangle::{Mesh2D, Vertex2D};
use iced_graphics::triangle::{Mesh2D, Shader, Vertex2D};
use iced_native::Renderer as _;
let b = layout.bounds();
// R O Y G B I V
let color_r = [1.0, 0.0, 0.0, 1.0];
let color_o = [1.0, 0.5, 0.0, 1.0];
let color_y = [1.0, 1.0, 0.0, 1.0];
let color_g = [0.0, 1.0, 0.0, 1.0];
let color_gb = [0.0, 1.0, 0.5, 1.0];
let color_b = [0.0, 0.2, 1.0, 1.0];
let color_i = [0.5, 0.0, 1.0, 1.0];
let color_v = [0.75, 0.0, 0.5, 1.0];
// let color_r = [1.0, 0.0, 0.0, 1.0];
// let color_o = [1.0, 0.5, 0.0, 1.0];
// let color_y = [1.0, 1.0, 0.0, 1.0];
// let color_g = [0.0, 1.0, 0.0, 1.0];
// let color_gb = [0.0, 1.0, 0.5, 1.0];
// let color_b = [0.0, 0.2, 1.0, 1.0];
// let color_i = [0.5, 0.0, 1.0, 1.0];
// let color_v = [0.75, 0.0, 0.5, 1.0];
let posn_center = {
if b.contains(cursor_position) {
@ -101,39 +105,39 @@ mod rainbow {
vertices: vec![
Vertex2D {
position: posn_center,
color: [1.0, 1.0, 1.0, 1.0],
// color: [1.0, 1.0, 1.0, 1.0],
},
Vertex2D {
position: posn_tl,
color: color_r,
// color: color_r,
},
Vertex2D {
position: posn_t,
color: color_o,
// color: color_o,
},
Vertex2D {
position: posn_tr,
color: color_y,
// color: color_y,
},
Vertex2D {
position: posn_r,
color: color_g,
// color: color_g,
},
Vertex2D {
position: posn_br,
color: color_gb,
// color: color_gb,
},
Vertex2D {
position: posn_b,
color: color_b,
// color: color_b,
},
Vertex2D {
position: posn_bl,
color: color_i,
// color: color_i,
},
Vertex2D {
position: posn_l,
color: color_v,
// color: color_v,
},
],
indices: vec![
@ -147,6 +151,7 @@ mod rainbow {
0, 8, 1, // L
],
},
shader: Shader::Solid(Color::BLACK),
};
renderer.with_translation(Vector::new(b.x, b.y), |renderer| {

View file

@ -0,0 +1,10 @@
[package]
name = "modern_art"
version = "0.1.0"
authors = ["Bingus <shankern@protonmail.com>"]
edition = "2021"
publish = false
[dependencies]
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
rand = "0.8.5"

View file

@ -0,0 +1,141 @@
use rand::{Rng, thread_rng};
use crate::canvas::{Cursor, FillStyle, Geometry, Gradient};
use iced::widget::canvas::{Cache, Fill, Frame};
use iced::widget::{canvas, Canvas};
use iced::Settings;
use iced::{
executor, Application, Color, Command, Element, Length, Point, Rectangle,
Renderer, Size, Theme,
};
fn main() -> iced::Result {
ModernArt::run(Settings {
antialiasing: true,
..Settings::default()
})
}
#[derive(Debug, Clone, Copy)]
enum Message {}
struct ModernArt {
cache: Cache,
}
impl Application for ModernArt {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = ();
fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
(
ModernArt {
cache: Default::default(),
},
Command::none(),
)
}
fn title(&self) -> String {
String::from("Modern Art")
}
fn update(&mut self, _message: Self::Message) -> Command<Self::Message> {
Command::none()
}
fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
Canvas::new(self)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
impl<Message> canvas::Program<Message> for ModernArt {
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 num_squares = thread_rng().gen_range(0..1200);
let mut i = 0;
while i <= num_squares {
generate_box(frame, bounds.size());
i += 1;
}
});
vec![geometry]
}
}
fn generate_box(frame: &mut Frame, bounds: Size) -> bool {
let solid = rand::random::<bool>();
let random_color = || -> Color {
Color::from_rgb(
thread_rng().gen_range(0.0..1.0),
thread_rng().gen_range(0.0..1.0),
thread_rng().gen_range(0.0..1.0),
)
};
let gradient = |top_left: Point, bottom_right: Point| -> Gradient {
let mut builder = Gradient::linear(top_left, bottom_right);
let stops = thread_rng().gen_range(1..64u32);
let mut i = 0;
while i <= stops {
builder = builder.add_stop(
i as f32 / stops as f32,
random_color()
);
i += 1;
}
builder.build().unwrap()
};
let top_left = Point::new(
thread_rng().gen_range(0.0..bounds.width),
thread_rng().gen_range(0.0..bounds.height)
);
let size = Size::new(
thread_rng().gen_range(50.0..200.0),
thread_rng().gen_range(50.0..200.0),
);
if solid {
frame.fill_rectangle(
top_left,
size,
Fill {
style: FillStyle::Solid(random_color()),
.. Default::default()
}
);
} else {
frame.fill_rectangle(
top_left,
size,
Fill {
style: FillStyle::Gradient(&gradient(
top_left,
Point::new(top_left.x + size.width, top_left.y + size.height)
)),
.. Default::default()
}
);
};
solid
}

View file

@ -19,6 +19,7 @@ use iced::{
};
use std::time::Instant;
use crate::canvas::StrokeStyle;
pub fn main() -> iced::Result {
SolarSystem::run(Settings {
@ -37,9 +38,9 @@ enum Message {
}
impl Application for SolarSystem {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Executor = executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
@ -65,10 +66,6 @@ impl Application for SolarSystem {
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
time::every(std::time::Duration::from_millis(10)).map(Message::Tick)
}
fn view(&self) -> Element<Message> {
canvas(&self.state)
.width(Length::Fill)
@ -86,6 +83,10 @@ impl Application for SolarSystem {
text_color: Color::WHITE,
})
}
fn subscription(&self) -> Subscription<Message> {
time::every(std::time::Duration::from_millis(10)).map(Message::Tick)
}
}
#[derive(Debug)]
@ -178,8 +179,8 @@ impl<Message> canvas::Program<Message> for State {
frame.stroke(
&orbit,
Stroke {
style: StrokeStyle::Solid(Color::from_rgba8(0, 153, 255, 0.1)),
width: 1.0,
color: Color::from_rgba8(0, 153, 255, 0.1),
line_dash: canvas::LineDash {
offset: 0,
segments: &[3.0, 6.0],