Write documentation for new `canvas` module
This commit is contained in:
parent
570f769744
commit
9c067562fa
|
@ -12,7 +12,7 @@ pub fn main() {
|
|||
|
||||
struct Clock {
|
||||
now: LocalTime,
|
||||
clock: canvas::layer::Cached<LocalTime>,
|
||||
clock: canvas::layer::Cache<LocalTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -28,7 +28,7 @@ impl Application for Clock {
|
|||
(
|
||||
Clock {
|
||||
now: chrono::Local::now().into(),
|
||||
clock: canvas::layer::Cached::new(),
|
||||
clock: canvas::layer::Cache::new(),
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
|
@ -91,7 +91,7 @@ impl From<chrono::DateTime<chrono::Local>> for LocalTime {
|
|||
}
|
||||
}
|
||||
|
||||
impl canvas::layer::Drawable for LocalTime {
|
||||
impl canvas::Drawable for LocalTime {
|
||||
fn draw(&self, frame: &mut canvas::Frame) {
|
||||
let center = frame.center();
|
||||
let radius = frame.width().min(frame.height()) / 2.0;
|
||||
|
|
|
@ -22,7 +22,7 @@ pub fn main() {
|
|||
|
||||
struct SolarSystem {
|
||||
state: State,
|
||||
solar_system: canvas::layer::Cached<State>,
|
||||
solar_system: canvas::layer::Cache<State>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -38,7 +38,7 @@ impl Application for SolarSystem {
|
|||
(
|
||||
SolarSystem {
|
||||
state: State::new(),
|
||||
solar_system: canvas::layer::Cached::new(),
|
||||
solar_system: canvas::layer::Cache::new(),
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
|
@ -125,7 +125,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
impl canvas::layer::Drawable for State {
|
||||
impl canvas::Drawable for State {
|
||||
fn draw(&self, frame: &mut canvas::Frame) {
|
||||
use canvas::{Fill, Path, Stroke};
|
||||
use std::f32::consts::PI;
|
||||
|
|
|
@ -179,7 +179,7 @@ pub trait Application: Sized {
|
|||
iced_wgpu::Settings {
|
||||
default_font: _settings.default_font,
|
||||
antialiasing: if _settings.use_antialiasing {
|
||||
Some(iced_wgpu::settings::MSAA::X4)
|
||||
Some(iced_wgpu::settings::Antialiasing::MSAAx4)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
|
||||
//! [WebGPU API]: https://gpuweb.github.io/gpuweb/
|
||||
//! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
|
||||
//#![deny(missing_docs)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![deny(unused_results)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
//! Configure a [`Renderer`].
|
||||
//!
|
||||
//! [`Renderer`]: struct.Renderer.html
|
||||
|
||||
/// The settings of a [`Renderer`].
|
||||
///
|
||||
/// [`Renderer`]: struct.Renderer.html
|
||||
/// [`Renderer`]: ../struct.Renderer.html
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Settings {
|
||||
/// The bytes of the font that will be used by default.
|
||||
|
@ -9,24 +13,29 @@ pub struct Settings {
|
|||
pub default_font: Option<&'static [u8]>,
|
||||
|
||||
/// The antialiasing strategy that will be used for triangle primitives.
|
||||
pub antialiasing: Option<MSAA>,
|
||||
pub antialiasing: Option<Antialiasing>,
|
||||
}
|
||||
|
||||
/// An antialiasing strategy.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MSAA {
|
||||
X2,
|
||||
X4,
|
||||
X8,
|
||||
X16,
|
||||
pub enum Antialiasing {
|
||||
/// Multisample AA with 2 samples
|
||||
MSAAx2,
|
||||
/// Multisample AA with 4 samples
|
||||
MSAAx4,
|
||||
/// Multisample AA with 8 samples
|
||||
MSAAx8,
|
||||
/// Multisample AA with 16 samples
|
||||
MSAAx16,
|
||||
}
|
||||
|
||||
impl MSAA {
|
||||
impl Antialiasing {
|
||||
pub(crate) fn sample_count(&self) -> u32 {
|
||||
match self {
|
||||
MSAA::X2 => 2,
|
||||
MSAA::X4 => 4,
|
||||
MSAA::X8 => 8,
|
||||
MSAA::X16 => 16,
|
||||
Antialiasing::MSAAx2 => 2,
|
||||
Antialiasing::MSAAx4 => 4,
|
||||
Antialiasing::MSAAx8 => 8,
|
||||
Antialiasing::MSAAx16 => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ impl<T> Buffer<T> {
|
|||
impl Pipeline {
|
||||
pub fn new(
|
||||
device: &mut wgpu::Device,
|
||||
antialiasing: Option<settings::MSAA>,
|
||||
antialiasing: Option<settings::Antialiasing>,
|
||||
) -> Pipeline {
|
||||
let constant_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
|
|
|
@ -10,7 +10,10 @@ pub struct Blit {
|
|||
}
|
||||
|
||||
impl Blit {
|
||||
pub fn new(device: &wgpu::Device, antialiasing: settings::MSAA) -> Blit {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
antialiasing: settings::Antialiasing,
|
||||
) -> Blit {
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
//! Draw freely in 2D.
|
||||
//! Draw 2D graphics for your users.
|
||||
//!
|
||||
//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a
|
||||
//! [`Frame`]. It can be used for animation, data visualization, game graphics,
|
||||
//! and more!
|
||||
//!
|
||||
//! [`Canvas`]: struct.Canvas.html
|
||||
//! [`Frame`]: struct.Frame.html
|
||||
use crate::{Defaults, Primitive, Renderer};
|
||||
|
||||
use iced_native::{
|
||||
|
@ -9,17 +16,26 @@ use std::hash::Hash;
|
|||
pub mod layer;
|
||||
pub mod path;
|
||||
|
||||
mod drawable;
|
||||
mod fill;
|
||||
mod frame;
|
||||
mod stroke;
|
||||
|
||||
pub use drawable::Drawable;
|
||||
pub use fill::Fill;
|
||||
pub use frame::Frame;
|
||||
pub use layer::Layer;
|
||||
pub use path::Path;
|
||||
pub use stroke::{LineCap, LineJoin, Stroke};
|
||||
|
||||
/// A 2D drawable region.
|
||||
/// A widget capable of drawing 2D graphics.
|
||||
///
|
||||
/// A [`Canvas`] may contain multiple layers. A [`Layer`] is drawn using the
|
||||
/// painter's algorithm. In other words, layers will be drawn on top of each in
|
||||
/// the same order they are pushed into the [`Canvas`].
|
||||
///
|
||||
/// [`Canvas`]: struct.Canvas.html
|
||||
/// [`Layer`]: layer/trait.Layer.html
|
||||
#[derive(Debug)]
|
||||
pub struct Canvas<'a> {
|
||||
width: Length,
|
||||
|
@ -30,6 +46,9 @@ pub struct Canvas<'a> {
|
|||
impl<'a> Canvas<'a> {
|
||||
const DEFAULT_SIZE: u16 = 100;
|
||||
|
||||
/// Creates a new [`Canvas`] with no layers.
|
||||
///
|
||||
/// [`Canvas`]: struct.Canvas.html
|
||||
pub fn new() -> Self {
|
||||
Canvas {
|
||||
width: Length::Units(Self::DEFAULT_SIZE),
|
||||
|
@ -38,16 +57,28 @@ impl<'a> Canvas<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets the width of the [`Canvas`].
|
||||
///
|
||||
/// [`Canvas`]: struct.Canvas.html
|
||||
pub fn width(mut self, width: Length) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the height of the [`Canvas`].
|
||||
///
|
||||
/// [`Canvas`]: struct.Canvas.html
|
||||
pub fn height(mut self, height: Length) -> Self {
|
||||
self.height = height;
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a [`Layer`] to the [`Canvas`].
|
||||
///
|
||||
/// It will be drawn on top of previous layers.
|
||||
///
|
||||
/// [`Layer`]: layer/trait.Layer.html
|
||||
/// [`Canvas`]: struct.Canvas.html
|
||||
pub fn push(mut self, layer: impl Layer + 'a) -> Self {
|
||||
self.layers.push(Box::new(layer));
|
||||
self
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
use crate::canvas::Frame;
|
||||
|
||||
/// A type that can be drawn on a [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
pub trait Drawable {
|
||||
/// Draws the [`Drawable`] on the given [`Frame`].
|
||||
///
|
||||
/// [`Drawable`]: trait.Drawable.html
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
fn draw(&self, frame: &mut Frame);
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
use iced_native::Color;
|
||||
|
||||
/// The style used to fill geometry.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Fill {
|
||||
/// Fill with a color.
|
||||
Color(Color),
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,14 @@ use crate::{
|
|||
triangle,
|
||||
};
|
||||
|
||||
/// The frame of a [`Canvas`].
|
||||
///
|
||||
/// [`Canvas`]: struct.Canvas.html
|
||||
#[derive(Debug)]
|
||||
pub struct Frame {
|
||||
width: f32,
|
||||
height: f32,
|
||||
buffers: lyon::tessellation::VertexBuffers<triangle::Vertex2D, u32>,
|
||||
|
||||
transforms: Transforms,
|
||||
}
|
||||
|
||||
|
@ -27,6 +29,12 @@ struct Transform {
|
|||
}
|
||||
|
||||
impl Frame {
|
||||
/// Creates a new empty [`Frame`] with the given dimensions.
|
||||
///
|
||||
/// The default coordinate system of a [`Frame`] has its origin at the
|
||||
/// top-left corner of its bounds.
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
pub fn new(width: f32, height: f32) -> Frame {
|
||||
Frame {
|
||||
width,
|
||||
|
@ -42,26 +50,43 @@ impl Frame {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the width of the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn width(&self) -> f32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
/// Returns the width of the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn height(&self) -> f32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
/// Returns the dimensions of the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn size(&self) -> Size {
|
||||
Size::new(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Returns the coordinate of the center of the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn center(&self) -> Point {
|
||||
Point::new(self.width / 2.0, self.height / 2.0)
|
||||
}
|
||||
|
||||
/// Draws the given [`Path`] on the [`Frame`] by filling it with the
|
||||
/// provided style.
|
||||
///
|
||||
/// [`Path`]: path/struct.Path.html
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
pub fn fill(&mut self, path: &Path, fill: Fill) {
|
||||
use lyon::tessellation::{
|
||||
BuffersBuilder, FillOptions, FillTessellator,
|
||||
|
@ -95,6 +120,11 @@ impl Frame {
|
|||
let _ = result.expect("Tessellate path");
|
||||
}
|
||||
|
||||
/// Draws the stroke of the given [`Path`] on the [`Frame`] with the
|
||||
/// provided style.
|
||||
///
|
||||
/// [`Path`]: path/struct.Path.html
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
pub fn stroke(&mut self, path: &Path, stroke: Stroke) {
|
||||
use lyon::tessellation::{
|
||||
BuffersBuilder, StrokeOptions, StrokeTessellator,
|
||||
|
@ -124,6 +154,13 @@ impl Frame {
|
|||
let _ = result.expect("Stroke path");
|
||||
}
|
||||
|
||||
/// Stores the current transform of the [`Frame`] and executes the given
|
||||
/// drawing operations, restoring the transform afterwards.
|
||||
///
|
||||
/// This method is useful to compose transforms and perform drawing
|
||||
/// operations in different coordinate systems.
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) {
|
||||
self.transforms.previous.push(self.transforms.current);
|
||||
|
@ -133,6 +170,9 @@ impl Frame {
|
|||
self.transforms.current = self.transforms.previous.pop().unwrap();
|
||||
}
|
||||
|
||||
/// Applies a translation to the current transform of the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn translate(&mut self, translation: Vector) {
|
||||
self.transforms.current.raw = self
|
||||
|
@ -146,6 +186,9 @@ impl Frame {
|
|||
self.transforms.current.is_identity = false;
|
||||
}
|
||||
|
||||
/// Applies a rotation to the current transform of the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn rotate(&mut self, angle: f32) {
|
||||
self.transforms.current.raw = self
|
||||
|
@ -156,6 +199,9 @@ impl Frame {
|
|||
self.transforms.current.is_identity = false;
|
||||
}
|
||||
|
||||
/// Applies a scaling to the current transform of the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
#[inline]
|
||||
pub fn scale(&mut self, scale: f32) {
|
||||
self.transforms.current.raw =
|
||||
|
@ -163,6 +209,9 @@ impl Frame {
|
|||
self.transforms.current.is_identity = false;
|
||||
}
|
||||
|
||||
/// Produces the geometry that has been drawn on the [`Frame`].
|
||||
///
|
||||
/// [`Frame`]: struct.Frame.html
|
||||
pub fn into_mesh(self) -> triangle::Mesh2D {
|
||||
triangle::Mesh2D {
|
||||
vertices: self.buffers.vertices,
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
mod cached;
|
||||
//! Produce, store, and reuse geometry.
|
||||
mod cache;
|
||||
|
||||
pub use cached::Cached;
|
||||
pub use cache::Cache;
|
||||
|
||||
use crate::{canvas::Frame, triangle};
|
||||
use crate::triangle;
|
||||
|
||||
use iced_native::Size;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A layer that can be presented at a [`Canvas`].
|
||||
///
|
||||
/// [`Canvas`]: ../struct.Canvas.html
|
||||
pub trait Layer: std::fmt::Debug {
|
||||
/// Draws the [`Layer`] in the given bounds and produces [`Mesh2D`] as a
|
||||
/// result.
|
||||
///
|
||||
/// The [`Layer`] may choose to store the produced [`Mesh2D`] locally and
|
||||
/// only recompute it when the bounds change, its contents change, or is
|
||||
/// otherwise explicitly cleared by other means.
|
||||
///
|
||||
/// [`Layer`]: trait.Layer.html
|
||||
/// [`Mesh2D`]: ../../../triangle/struct.Mesh2D.html
|
||||
fn draw(&self, bounds: Size) -> Arc<triangle::Mesh2D>;
|
||||
}
|
||||
|
||||
pub trait Drawable {
|
||||
fn draw(&self, frame: &mut Frame);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
canvas::{layer::Drawable, Frame, Layer},
|
||||
canvas::{Drawable, Frame, Layer},
|
||||
triangle,
|
||||
};
|
||||
|
||||
|
@ -8,14 +8,21 @@ use std::cell::RefCell;
|
|||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A simple cache that stores generated geometry to avoid recomputation.
|
||||
///
|
||||
/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer
|
||||
/// change or it is explicitly cleared.
|
||||
///
|
||||
/// [`Layer`]: ../trait.Layer.html
|
||||
/// [`Cached`]: struct.Cached.html
|
||||
#[derive(Debug)]
|
||||
pub struct Cached<T: Drawable> {
|
||||
pub struct Cache<T: Drawable> {
|
||||
input: PhantomData<T>,
|
||||
cache: RefCell<Cache>,
|
||||
state: RefCell<State>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Cache {
|
||||
enum State {
|
||||
Empty,
|
||||
Filled {
|
||||
mesh: Arc<triangle::Mesh2D>,
|
||||
|
@ -23,24 +30,36 @@ enum Cache {
|
|||
},
|
||||
}
|
||||
|
||||
impl<T> Cached<T>
|
||||
impl<T> Cache<T>
|
||||
where
|
||||
T: Drawable + std::fmt::Debug,
|
||||
{
|
||||
/// Creates a new empty [`Cache`].
|
||||
///
|
||||
/// [`Cache`]: struct.Cache.html
|
||||
pub fn new() -> Self {
|
||||
Cached {
|
||||
Cache {
|
||||
input: PhantomData,
|
||||
cache: RefCell::new(Cache::Empty),
|
||||
state: RefCell::new(State::Empty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the cache, forcing a redraw the next time it is used.
|
||||
///
|
||||
/// [`Cached`]: struct.Cached.html
|
||||
pub fn clear(&mut self) {
|
||||
*self.cache.borrow_mut() = Cache::Empty;
|
||||
*self.state.borrow_mut() = State::Empty;
|
||||
}
|
||||
|
||||
/// Binds the [`Cache`] with some data, producing a [`Layer`] that can be
|
||||
/// added to a [`Canvas`].
|
||||
///
|
||||
/// [`Cache`]: struct.Cache.html
|
||||
/// [`Layer`]: ../trait.Layer.html
|
||||
/// [`Canvas`]: ../../struct.Canvas.html
|
||||
pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a {
|
||||
Bind {
|
||||
layer: self,
|
||||
cache: self,
|
||||
input: input,
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +67,7 @@ where
|
|||
|
||||
#[derive(Debug)]
|
||||
struct Bind<'a, T: Drawable> {
|
||||
layer: &'a Cached<T>,
|
||||
cache: &'a Cache<T>,
|
||||
input: &'a T,
|
||||
}
|
||||
|
||||
|
@ -59,8 +78,8 @@ where
|
|||
fn draw(&self, current_bounds: Size) -> Arc<triangle::Mesh2D> {
|
||||
use std::ops::Deref;
|
||||
|
||||
if let Cache::Filled { mesh, bounds } =
|
||||
self.layer.cache.borrow().deref()
|
||||
if let State::Filled { mesh, bounds } =
|
||||
self.cache.state.borrow().deref()
|
||||
{
|
||||
if *bounds == current_bounds {
|
||||
return mesh.clone();
|
||||
|
@ -72,7 +91,7 @@ where
|
|||
|
||||
let mesh = Arc::new(frame.into_mesh());
|
||||
|
||||
*self.layer.cache.borrow_mut() = Cache::Filled {
|
||||
*self.cache.state.borrow_mut() = State::Filled {
|
||||
mesh: mesh.clone(),
|
||||
bounds: current_bounds,
|
||||
};
|
|
@ -1,13 +1,28 @@
|
|||
use iced_native::{Point, Size, Vector};
|
||||
//! Build different kinds of 2D shapes.
|
||||
pub mod arc;
|
||||
|
||||
use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder};
|
||||
mod builder;
|
||||
|
||||
pub use arc::Arc;
|
||||
pub use builder::Builder;
|
||||
|
||||
/// An immutable set of points that may or may not be connected.
|
||||
///
|
||||
/// A single [`Path`] can represent different kinds of 2D shapes!
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Path {
|
||||
raw: lyon::path::Path,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Creates a new [`Path`] with the provided closure.
|
||||
///
|
||||
/// Use the [`Builder`] to configure your [`Path`].
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
pub fn new(f: impl FnOnce(&mut Builder)) -> Self {
|
||||
let mut builder = Builder::new();
|
||||
|
||||
|
@ -32,152 +47,3 @@ impl Path {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Builder {
|
||||
raw: lyon::path::builder::SvgPathBuilder<lyon::path::Builder>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
raw: lyon::path::Path::builder().with_svg(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn move_to(&mut self, point: Point) {
|
||||
let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line_to(&mut self, point: Point) {
|
||||
let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn arc(&mut self, arc: Arc) {
|
||||
self.ellipse(arc.into());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if self.raw.current_position() != a {
|
||||
let _ = self.raw.line_to(a);
|
||||
}
|
||||
|
||||
let _ = self.raw.arc_to(
|
||||
math::Vector::new(radius, radius),
|
||||
math::Angle::radians(0.0),
|
||||
path::ArcFlags::default(),
|
||||
math::Point::new(b.x, b.y),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn ellipse(&mut self, ellipse: Ellipse) {
|
||||
use lyon::{geom, math};
|
||||
|
||||
let arc = geom::Arc {
|
||||
center: math::Point::new(ellipse.center.x, ellipse.center.y),
|
||||
radii: math::Vector::new(ellipse.radii.x, ellipse.radii.y),
|
||||
x_rotation: math::Angle::radians(ellipse.rotation),
|
||||
start_angle: math::Angle::radians(ellipse.start_angle),
|
||||
sweep_angle: math::Angle::radians(ellipse.end_angle),
|
||||
};
|
||||
|
||||
let _ = self.raw.move_to(arc.sample(0.0));
|
||||
|
||||
arc.for_each_quadratic_bezier(&mut |curve| {
|
||||
let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bezier_curve_to(
|
||||
&mut self,
|
||||
control_a: Point,
|
||||
control_b: Point,
|
||||
to: Point,
|
||||
) {
|
||||
use lyon::math;
|
||||
|
||||
let _ = self.raw.cubic_bezier_to(
|
||||
math::Point::new(control_a.x, control_a.y),
|
||||
math::Point::new(control_b.x, control_b.y),
|
||||
math::Point::new(to.x, to.y),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
|
||||
use lyon::math;
|
||||
|
||||
let _ = self.raw.quadratic_bezier_to(
|
||||
math::Point::new(control.x, control.y),
|
||||
math::Point::new(to.x, to.y),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rectangle(&mut self, p: Point, size: Size) {
|
||||
self.move_to(p);
|
||||
self.line_to(Point::new(p.x + size.width, p.y));
|
||||
self.line_to(Point::new(p.x + size.width, p.y + size.height));
|
||||
self.line_to(Point::new(p.x, p.y + size.height));
|
||||
self.close();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn circle(&mut self, center: Point, radius: f32) {
|
||||
self.arc(Arc {
|
||||
center,
|
||||
radius,
|
||||
start_angle: 0.0,
|
||||
end_angle: 2.0 * std::f32::consts::PI,
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn close(&mut self) {
|
||||
self.raw.close()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build(self) -> Path {
|
||||
Path {
|
||||
raw: self.raw.build(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Arc {
|
||||
pub center: Point,
|
||||
pub radius: f32,
|
||||
pub start_angle: f32,
|
||||
pub end_angle: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Ellipse {
|
||||
pub center: Point,
|
||||
pub radii: Vector,
|
||||
pub rotation: f32,
|
||||
pub start_angle: f32,
|
||||
pub end_angle: f32,
|
||||
}
|
||||
|
||||
impl From<Arc> for Ellipse {
|
||||
fn from(arc: Arc) -> Ellipse {
|
||||
Ellipse {
|
||||
center: arc.center,
|
||||
radii: Vector::new(arc.radius, arc.radius),
|
||||
rotation: 0.0,
|
||||
start_angle: arc.start_angle,
|
||||
end_angle: arc.end_angle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
//! Build and draw curves.
|
||||
use iced_native::{Point, Vector};
|
||||
|
||||
/// A segment of a differentiable curve.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Arc {
|
||||
/// The center of the arc.
|
||||
pub center: Point,
|
||||
/// The radius of the arc.
|
||||
pub radius: f32,
|
||||
/// The start of the segment's angle, clockwise rotation.
|
||||
pub start_angle: f32,
|
||||
/// The end of the segment's angle, clockwise rotation.
|
||||
pub end_angle: f32,
|
||||
}
|
||||
|
||||
/// An elliptical [`Arc`].
|
||||
///
|
||||
/// [`Arc`]: struct.Arc.html
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Elliptical {
|
||||
/// The center of the arc.
|
||||
pub center: Point,
|
||||
/// The radii of the arc's ellipse, defining its axes.
|
||||
pub radii: Vector,
|
||||
/// The rotation of the arc's ellipse.
|
||||
pub rotation: f32,
|
||||
/// The start of the segment's angle, clockwise rotation.
|
||||
pub start_angle: f32,
|
||||
/// The end of the segment's angle, clockwise rotation.
|
||||
pub end_angle: f32,
|
||||
}
|
||||
|
||||
impl From<Arc> for Elliptical {
|
||||
fn from(arc: Arc) -> Elliptical {
|
||||
Elliptical {
|
||||
center: arc.center,
|
||||
radii: Vector::new(arc.radius, arc.radius),
|
||||
rotation: 0.0,
|
||||
start_angle: arc.start_angle,
|
||||
end_angle: arc.end_angle,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
use crate::canvas::path::{arc, Arc, Path};
|
||||
|
||||
use iced_native::{Point, Size};
|
||||
use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder};
|
||||
|
||||
/// A [`Path`] builder.
|
||||
///
|
||||
/// Once a [`Path`] is built, it can no longer be mutated.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Builder {
|
||||
raw: lyon::path::builder::SvgPathBuilder<lyon::path::Builder>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new [`Builder`].
|
||||
///
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
raw: lyon::path::Path::builder().with_svg(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves the starting point of a new sub-path to the given `Point`.
|
||||
#[inline]
|
||||
pub fn move_to(&mut self, point: Point) {
|
||||
let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y));
|
||||
}
|
||||
|
||||
/// Connects the last point in the [`Path`] to the given `Point` with a
|
||||
/// straight line.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[inline]
|
||||
pub fn line_to(&mut self, point: Point) {
|
||||
let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y));
|
||||
}
|
||||
|
||||
/// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in
|
||||
/// a clockwise direction.
|
||||
///
|
||||
/// [`Arc`]: struct.Arc.html
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[inline]
|
||||
pub fn arc(&mut self, arc: Arc) {
|
||||
self.ellipse(arc.into());
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
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);
|
||||
|
||||
if self.raw.current_position() != a {
|
||||
let _ = self.raw.line_to(a);
|
||||
}
|
||||
|
||||
let _ = self.raw.arc_to(
|
||||
math::Vector::new(radius, radius),
|
||||
math::Angle::radians(0.0),
|
||||
path::ArcFlags::default(),
|
||||
math::Point::new(b.x, b.y),
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds an [`Ellipse`] to the [`Path`] using a clockwise direction.
|
||||
///
|
||||
/// [`Ellipse`]: struct.Arc.html
|
||||
/// [`Path`]: struct.Path.html
|
||||
pub fn ellipse(&mut self, arc: arc::Elliptical) {
|
||||
use lyon::{geom, math};
|
||||
|
||||
let arc = geom::Arc {
|
||||
center: math::Point::new(arc.center.x, arc.center.y),
|
||||
radii: math::Vector::new(arc.radii.x, arc.radii.y),
|
||||
x_rotation: math::Angle::radians(arc.rotation),
|
||||
start_angle: math::Angle::radians(arc.start_angle),
|
||||
sweep_angle: math::Angle::radians(arc.end_angle),
|
||||
};
|
||||
|
||||
let _ = self.raw.move_to(arc.sample(0.0));
|
||||
|
||||
arc.for_each_quadratic_bezier(&mut |curve| {
|
||||
let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds a cubic Bézier curve to the [`Path`] given its two control points
|
||||
/// and its end point.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[inline]
|
||||
pub fn bezier_curve_to(
|
||||
&mut self,
|
||||
control_a: Point,
|
||||
control_b: Point,
|
||||
to: Point,
|
||||
) {
|
||||
use lyon::math;
|
||||
|
||||
let _ = self.raw.cubic_bezier_to(
|
||||
math::Point::new(control_a.x, control_a.y),
|
||||
math::Point::new(control_b.x, control_b.y),
|
||||
math::Point::new(to.x, to.y),
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a quadratic Bézier curve to the [`Path`] given its control point
|
||||
/// and its end point.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[inline]
|
||||
pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
|
||||
use lyon::math;
|
||||
|
||||
let _ = self.raw.quadratic_bezier_to(
|
||||
math::Point::new(control.x, control.y),
|
||||
math::Point::new(to.x, to.y),
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a rectangle to the [`Path`] given its top-left corner coordinate
|
||||
/// and its `Size`.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[inline]
|
||||
pub fn rectangle(&mut self, p: Point, size: Size) {
|
||||
self.move_to(p);
|
||||
self.line_to(Point::new(p.x + size.width, p.y));
|
||||
self.line_to(Point::new(p.x + size.width, p.y + size.height));
|
||||
self.line_to(Point::new(p.x, p.y + size.height));
|
||||
self.close();
|
||||
}
|
||||
|
||||
/// Adds a circle to the [`Path`] given its center coordinate and its
|
||||
/// radius.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[inline]
|
||||
pub fn circle(&mut self, center: Point, radius: f32) {
|
||||
self.arc(Arc {
|
||||
center,
|
||||
radius,
|
||||
start_angle: 0.0,
|
||||
end_angle: 2.0 * std::f32::consts::PI,
|
||||
});
|
||||
}
|
||||
|
||||
/// Closes the current sub-path in the [`Path`] with a straight line to
|
||||
/// the starting point.
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
#[inline]
|
||||
pub fn close(&mut self) {
|
||||
self.raw.close()
|
||||
}
|
||||
|
||||
/// Builds the [`Path`] of this [`Builder`].
|
||||
///
|
||||
/// [`Path`]: struct.Path.html
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
#[inline]
|
||||
pub fn build(self) -> Path {
|
||||
Path {
|
||||
raw: self.raw.build(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,16 @@
|
|||
use iced_native::Color;
|
||||
|
||||
/// The style of a stroke.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Stroke {
|
||||
/// The color of the stroke.
|
||||
pub color: Color,
|
||||
/// The distance between the two edges of the stroke.
|
||||
pub width: f32,
|
||||
/// The shape to be used at the end of open subpaths when they are stroked.
|
||||
pub line_cap: LineCap,
|
||||
/// The shape to be used at the corners of paths or basic shapes when they
|
||||
/// are stroked.
|
||||
pub line_join: LineJoin,
|
||||
}
|
||||
|
||||
|
@ -19,10 +25,16 @@ impl Default for Stroke {
|
|||
}
|
||||
}
|
||||
|
||||
/// The shape used at the end of open subpaths when they are stroked.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LineCap {
|
||||
/// The stroke for each sub-path does not extend beyond its two endpoints.
|
||||
Butt,
|
||||
/// At the end of each sub-path, the shape representing the stroke will be
|
||||
/// extended by a square.
|
||||
Square,
|
||||
/// At the end of each sub-path, the shape representing the stroke will be
|
||||
/// extended by a semicircle.
|
||||
Round,
|
||||
}
|
||||
|
||||
|
@ -42,10 +54,15 @@ impl From<LineCap> for lyon::tessellation::LineCap {
|
|||
}
|
||||
}
|
||||
|
||||
/// The shape used at the corners of paths or basic shapes when they are
|
||||
/// stroked.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LineJoin {
|
||||
/// A sharp corner.
|
||||
Miter,
|
||||
/// A round corner.
|
||||
Round,
|
||||
/// A bevelled corner.
|
||||
Bevel,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue