diff options
Diffstat (limited to 'graphics')
| -rw-r--r-- | graphics/Cargo.toml | 4 | ||||
| -rw-r--r-- | graphics/src/backend.rs | 12 | ||||
| -rw-r--r-- | graphics/src/color.rs | 46 | ||||
| -rw-r--r-- | graphics/src/compositor.rs | 15 | ||||
| -rw-r--r-- | graphics/src/damage.rs | 5 | ||||
| -rw-r--r-- | graphics/src/geometry.rs | 17 | ||||
| -rw-r--r-- | graphics/src/geometry/fill.rs | 14 | ||||
| -rw-r--r-- | graphics/src/geometry/path.rs | 2 | ||||
| -rw-r--r-- | graphics/src/geometry/style.rs | 3 | ||||
| -rw-r--r-- | graphics/src/gradient.rs | 195 | ||||
| -rw-r--r-- | graphics/src/image.rs | 1 | ||||
| -rw-r--r-- | graphics/src/lib.rs | 8 | ||||
| -rw-r--r-- | graphics/src/primitive.rs | 49 | ||||
| -rw-r--r-- | graphics/src/renderer.rs | 32 | ||||
| -rw-r--r-- | graphics/src/triangle.rs | 1 | 
15 files changed, 338 insertions, 66 deletions
| diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 125ea17d..02621695 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -14,9 +14,11 @@ categories = ["gui"]  geometry = ["lyon_path"]  opengl = []  image = ["dep:image", "kamadak-exif"] +web-colors = []  [dependencies] -glam = "0.21.3" +glam = "0.24" +half = "2.2.1"  log = "0.4"  raw-window-handle = "0.5"  thiserror = "1.0" diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index ae89da06..6a082576 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -6,18 +6,6 @@ use iced_core::{Font, Point, Size};  use std::borrow::Cow; -/// The graphics backend of a [`Renderer`]. -/// -/// [`Renderer`]: crate::Renderer -pub trait Backend { -    /// Trims the measurements cache. -    /// -    /// This method is currently necessary to properly trim the text cache in -    /// `iced_wgpu` and `iced_glow` because of limitations in the text rendering -    /// pipeline. It will be removed in the future. -    fn trim_measurements(&mut self) {} -} -  /// A graphics backend that supports text rendering.  pub trait Text {      /// The icon font of the backend. diff --git a/graphics/src/color.rs b/graphics/src/color.rs new file mode 100644 index 00000000..92448a68 --- /dev/null +++ b/graphics/src/color.rs @@ -0,0 +1,46 @@ +//! Manage colors for shaders. +use crate::core::Color; + +use bytemuck::{Pod, Zeroable}; + +/// A color packed as 4 floats representing RGBA channels. +#[derive(Debug, Clone, Copy, PartialEq, Zeroable, Pod)] +#[repr(C)] +pub struct Packed([f32; 4]); + +impl Packed { +    /// Returns the internal components of the [`Packed`] color. +    pub fn components(self) -> [f32; 4] { +        self.0 +    } +} + +/// A flag that indicates whether the renderer should perform gamma correction. +pub const GAMMA_CORRECTION: bool = internal::GAMMA_CORRECTION; + +/// Packs a [`Color`]. +pub fn pack(color: impl Into<Color>) -> Packed { +    Packed(internal::pack(color.into())) +} + +#[cfg(not(feature = "web-colors"))] +mod internal { +    use crate::core::Color; + +    pub const GAMMA_CORRECTION: bool = true; + +    pub fn pack(color: Color) -> [f32; 4] { +        color.into_linear() +    } +} + +#[cfg(feature = "web-colors")] +mod internal { +    use crate::core::Color; + +    pub const GAMMA_CORRECTION: bool = false; + +    pub fn pack(color: Color) -> [f32; 4] { +        [color.r, color.g, color.b, color.a] +    } +} diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index d55e801a..f7b86045 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -59,6 +59,19 @@ pub trait Compositor: Sized {          background_color: Color,          overlay: &[T],      ) -> Result<(), SurfaceError>; + +    /// Screenshots the current [`Renderer`] primitives to an offscreen texture, and returns the bytes of +    /// the texture ordered as `RGBA` in the sRGB color space. +    /// +    /// [`Renderer`]: Self::Renderer; +    fn screenshot<T: AsRef<str>>( +        &mut self, +        renderer: &mut Self::Renderer, +        surface: &mut Self::Surface, +        viewport: &Viewport, +        background_color: Color, +        overlay: &[T], +    ) -> Vec<u8>;  }  /// Result of an unsuccessful call to [`Compositor::present`]. @@ -82,7 +95,7 @@ pub enum SurfaceError {      OutOfMemory,  } -/// Contains informations about the graphics (e.g. graphics adapter, graphics backend). +/// Contains information about the graphics (e.g. graphics adapter, graphics backend).  #[derive(Debug)]  pub struct Information {      /// Contains the graphics adapter. diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs index 5aab06b1..c6b0f759 100644 --- a/graphics/src/damage.rs +++ b/graphics/src/damage.rs @@ -1,8 +1,10 @@ +//! Track and compute the damage of graphical primitives.  use crate::core::{Rectangle, Size};  use crate::Primitive;  use std::sync::Arc; +/// Computes the damage regions between the two given primitives.  pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {      match (a, b) {          ( @@ -73,6 +75,7 @@ pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {      }  } +/// Computes the damage regions between the two given lists of primitives.  pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {      let damage = previous          .iter() @@ -95,6 +98,8 @@ pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {      }  } +/// Groups the given damage regions that are close together inside the given +/// bounds.  pub fn group(      mut damage: Vec<Rectangle>,      scale_factor: f32, diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 8db1594a..729c3d44 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -1,8 +1,4 @@ -//! 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! +//! Build and draw geometry.  pub mod fill;  pub mod path;  pub mod stroke; @@ -16,10 +12,11 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke};  pub use style::Style;  pub use text::Text; -pub use iced_core::gradient::{self, Gradient}; +pub use crate::gradient::{self, Gradient};  use crate::Primitive; +/// A bunch of shapes that can be drawn.  #[derive(Debug, Clone)]  pub struct Geometry(pub Primitive); @@ -29,8 +26,8 @@ impl From<Geometry> for Primitive {      }  } -pub trait Renderer: iced_core::Renderer { -    type Geometry; - -    fn draw(&mut self, geometry: Vec<Self::Geometry>); +/// A renderer capable of drawing some [`Geometry`]. +pub trait Renderer: crate::core::Renderer { +    /// Draws the given layers of [`Geometry`]. +    fn draw(&mut self, layers: Vec<Geometry>);  } diff --git a/graphics/src/geometry/fill.rs b/graphics/src/geometry/fill.rs index 2e8c1669..b773c99b 100644 --- a/graphics/src/geometry/fill.rs +++ b/graphics/src/geometry/fill.rs @@ -1,8 +1,9 @@  //! Fill [crate::widget::canvas::Geometry] with a certain style. -use iced_core::{Color, Gradient}; -  pub use crate::geometry::Style; +use crate::core::Color; +use crate::gradient::{self, Gradient}; +  /// The style used to fill geometry.  #[derive(Debug, Clone)]  pub struct Fill { @@ -49,6 +50,15 @@ impl From<Gradient> for Fill {      }  } +impl From<gradient::Linear> for Fill { +    fn from(gradient: gradient::Linear) -> Self { +        Fill { +            style: Style::Gradient(Gradient::Linear(gradient)), +            ..Default::default() +        } +    } +} +  /// The fill rule defines how to determine what is inside and what is outside of  /// a shape.  /// diff --git a/graphics/src/geometry/path.rs b/graphics/src/geometry/path.rs index c3127bdf..3d8fc6fa 100644 --- a/graphics/src/geometry/path.rs +++ b/graphics/src/geometry/path.rs @@ -53,11 +53,13 @@ impl Path {          Self::new(|p| p.circle(center, radius))      } +    /// Returns the internal [`lyon_path::Path`].      #[inline]      pub fn raw(&self) -> &lyon_path::Path {          &self.raw      } +    /// Returns the current [`Path`] with the given transform applied to it.      #[inline]      pub fn transform(&self, transform: &lyon_path::math::Transform) -> Path {          Path { diff --git a/graphics/src/geometry/style.rs b/graphics/src/geometry/style.rs index be9ee376..a0f4b08a 100644 --- a/graphics/src/geometry/style.rs +++ b/graphics/src/geometry/style.rs @@ -1,4 +1,5 @@ -use iced_core::{Color, Gradient}; +use crate::core::Color; +use crate::geometry::Gradient;  /// The coloring style of some drawing.  #[derive(Debug, Clone, PartialEq)] diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs new file mode 100644 index 00000000..3f5d0509 --- /dev/null +++ b/graphics/src/gradient.rs @@ -0,0 +1,195 @@ +//! A gradient that can be used as a [`Fill`] for some geometry. +//! +//! For a gradient that you can use as a background variant for a widget, see [`Gradient`]. +//! +//! [`Gradient`]: crate::core::Gradient; +use crate::color; +use crate::core::gradient::ColorStop; +use crate::core::{self, Color, Point, Rectangle}; + +use half::f16; +use std::cmp::Ordering; + +#[derive(Debug, Clone, PartialEq)] +/// A fill which linearly interpolates colors along a direction. +/// +/// For a gradient which can be used as a fill for a background of a widget, see [`crate::core::Gradient`]. +pub enum Gradient { +    /// A linear gradient interpolates colors along a direction from its `start` to its `end` +    /// point. +    Linear(Linear), +} + +impl From<Linear> for Gradient { +    fn from(gradient: Linear) -> Self { +        Self::Linear(gradient) +    } +} + +impl Gradient { +    /// Packs the [`Gradient`] for use in shader code. +    pub fn pack(&self) -> Packed { +        match self { +            Gradient::Linear(linear) => linear.pack(), +        } +    } +} + +/// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`]. +/// +/// [`Fill`]: crate::geometry::Fill; +/// [`Stroke`]: crate::geometry::Stroke; +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Linear { +    /// The absolute starting position of the gradient. +    pub start: Point, + +    /// The absolute ending position of the gradient. +    pub end: Point, + +    /// [`ColorStop`]s along the linear gradient direction. +    pub stops: [Option<ColorStop>; 8], +} + +impl Linear { +    /// Creates a new [`Builder`]. +    pub fn new(start: Point, end: Point) -> Self { +        Self { +            start, +            end, +            stops: [None; 8], +        } +    } + +    /// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient. +    /// +    /// Any `offset` that is not within `0.0..=1.0` will be silently ignored. +    /// +    /// Any stop added after the 8th will be silently ignored. +    pub fn add_stop(mut self, offset: f32, color: Color) -> Self { +        if offset.is_finite() && (0.0..=1.0).contains(&offset) { +            let (Ok(index) | Err(index)) = +                self.stops.binary_search_by(|stop| match stop { +                    None => Ordering::Greater, +                    Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), +                }); + +            if index < 8 { +                self.stops[index] = Some(ColorStop { offset, color }); +            } +        } else { +            log::warn!("Gradient: ColorStop must be within 0.0..=1.0 range."); +        }; + +        self +    } + +    /// Adds multiple [`ColorStop`]s to the gradient. +    /// +    /// Any stop added after the 8th will be silently ignored. +    pub fn add_stops( +        mut self, +        stops: impl IntoIterator<Item = ColorStop>, +    ) -> Self { +        for stop in stops.into_iter() { +            self = self.add_stop(stop.offset, stop.color) +        } + +        self +    } + +    /// Packs the [`Gradient`] for use in shader code. +    pub fn pack(&self) -> Packed { +        let mut colors = [[0u32; 2]; 8]; +        let mut offsets = [f16::from(0u8); 8]; + +        for (index, stop) in self.stops.iter().enumerate() { +            let [r, g, b, a] = +                color::pack(stop.map_or(Color::default(), |s| s.color)) +                    .components(); + +            colors[index] = [ +                pack_f16s([f16::from_f32(r), f16::from_f32(g)]), +                pack_f16s([f16::from_f32(b), f16::from_f32(a)]), +            ]; + +            offsets[index] = +                stop.map_or(f16::from_f32(2.0), |s| f16::from_f32(s.offset)); +        } + +        let offsets = [ +            pack_f16s([offsets[0], offsets[1]]), +            pack_f16s([offsets[2], offsets[3]]), +            pack_f16s([offsets[4], offsets[5]]), +            pack_f16s([offsets[6], offsets[7]]), +        ]; + +        let direction = [self.start.x, self.start.y, self.end.x, self.end.y]; + +        Packed { +            colors, +            offsets, +            direction, +        } +    } +} + +/// Packed [`Gradient`] data for use in shader code. +#[derive(Debug, Copy, Clone, PartialEq)] +#[repr(C)] +pub struct Packed { +    // 8 colors, each channel = 16 bit float, 2 colors packed into 1 u32 +    colors: [[u32; 2]; 8], +    // 8 offsets, 8x 16 bit floats packed into 4 u32s +    offsets: [u32; 4], +    direction: [f32; 4], +} + +/// Creates a new [`Packed`] gradient for use in shader code. +pub fn pack(gradient: &core::Gradient, bounds: Rectangle) -> Packed { +    match gradient { +        core::Gradient::Linear(linear) => { +            let mut colors = [[0u32; 2]; 8]; +            let mut offsets = [f16::from(0u8); 8]; + +            for (index, stop) in linear.stops.iter().enumerate() { +                let [r, g, b, a] = +                    color::pack(stop.map_or(Color::default(), |s| s.color)) +                        .components(); + +                colors[index] = [ +                    pack_f16s([f16::from_f32(r), f16::from_f32(g)]), +                    pack_f16s([f16::from_f32(b), f16::from_f32(a)]), +                ]; + +                offsets[index] = stop +                    .map_or(f16::from_f32(2.0), |s| f16::from_f32(s.offset)); +            } + +            let offsets = [ +                pack_f16s([offsets[0], offsets[1]]), +                pack_f16s([offsets[2], offsets[3]]), +                pack_f16s([offsets[4], offsets[5]]), +                pack_f16s([offsets[6], offsets[7]]), +            ]; + +            let (start, end) = linear.angle.to_distance(&bounds); + +            let direction = [start.x, start.y, end.x, end.y]; + +            Packed { +                colors, +                offsets, +                direction, +            } +        } +    } +} + +/// Packs two f16s into one u32. +fn pack_f16s(f: [f16; 2]) -> u32 { +    let one = (f[0].to_bits() as u32) << 16; +    let two = f[1].to_bits() as u32; + +    one | two +} diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 2f634252..6b43f4a8 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -5,6 +5,7 @@ use bitflags::bitflags;  pub use ::image as image_rs; +/// Tries to load an image by its [`Handle`].  pub fn load(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> {      match handle.data() {          Data::Path(path) => { diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index e3de4025..226b245b 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -9,7 +9,7 @@  )]  #![deny(      missing_debug_implementations, -    //missing_docs, +    missing_docs,      unsafe_code,      unused_results,      clippy::extra_unused_lifetimes, @@ -20,15 +20,17 @@  )]  #![forbid(rust_2018_idioms)]  #![allow(clippy::inherent_to_string, clippy::type_complexity)] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))]  mod antialiasing;  mod error;  mod transformation;  mod viewport;  pub mod backend; +pub mod color;  pub mod compositor;  pub mod damage; +pub mod gradient;  pub mod primitive;  pub mod renderer; @@ -39,9 +41,9 @@ pub mod geometry;  pub mod image;  pub use antialiasing::Antialiasing; -pub use backend::Backend;  pub use compositor::Compositor;  pub use error::Error; +pub use gradient::Gradient;  pub use primitive::Primitive;  pub use renderer::Renderer;  pub use transformation::Transformation; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index d814c757..187c6c6c 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -1,8 +1,11 @@ +//! Draw using different graphical primitives. +use crate::color;  use crate::core::alignment;  use crate::core::image;  use crate::core::svg;  use crate::core::text; -use crate::core::{Background, Color, Font, Gradient, Rectangle, Size, Vector}; +use crate::core::{Background, Color, Font, Rectangle, Size, Vector}; +use crate::gradient;  use bytemuck::{Pod, Zeroable};  use std::sync::Arc; @@ -38,7 +41,7 @@ pub enum Primitive {          bounds: Rectangle,          /// The background of the quad          background: Background, -        /// The border radius of the quad +        /// The border radii of the quad          border_radius: [f32; 4],          /// The border width of the quad          border_width: f32, @@ -80,28 +83,35 @@ pub enum Primitive {      /// It can be used to render many kinds of geometry freely.      GradientMesh {          /// The vertices and indices of the mesh. -        buffers: Mesh2D<Vertex2D>, +        buffers: Mesh2D<GradientVertex2D>,          /// The size of the drawable region of the mesh.          ///          /// Any geometry that falls out of this region will be clipped.          size: Size, - -        /// The [`Gradient`] to apply to the mesh. -        gradient: Gradient,      }, +    /// A [`tiny_skia`] path filled with some paint.      #[cfg(feature = "tiny-skia")]      Fill { +        /// The path to fill.          path: tiny_skia::Path, +        /// The paint to use.          paint: tiny_skia::Paint<'static>, +        /// The fill rule to follow.          rule: tiny_skia::FillRule, +        /// The transform to apply to the path.          transform: tiny_skia::Transform,      }, +    /// A [`tiny_skia`] path stroked with some paint.      #[cfg(feature = "tiny-skia")]      Stroke { +        /// The path to stroke.          path: tiny_skia::Path, +        /// The paint to use.          paint: tiny_skia::Paint<'static>, +        /// The stroke settings.          stroke: tiny_skia::Stroke, +        /// The transform to apply to the path.          transform: tiny_skia::Transform,      },      /// A group of primitives @@ -135,10 +145,12 @@ pub enum Primitive {  }  impl Primitive { +    /// Creates a [`Primitive::Group`].      pub fn group(primitives: Vec<Self>) -> Self {          Self::Group { primitives }      } +    /// Creates a [`Primitive::Clip`].      pub fn clip(self, bounds: Rectangle) -> Self {          Self::Clip {              bounds, @@ -146,6 +158,7 @@ impl Primitive {          }      } +    /// Creates a [`Primitive::Translate`].      pub fn translate(self, translation: Vector) -> Self {          Self::Translate {              translation, @@ -153,6 +166,7 @@ impl Primitive {          }      } +    /// Returns the bounds of the [`Primitive`].      pub fn bounds(&self) -> Rectangle {          match self {              Self::Text { @@ -227,25 +241,34 @@ pub struct Mesh2D<T> {      pub indices: Vec<u32>,  } -/// A two-dimensional vertex. +/// A two-dimensional vertex with a color.  #[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]  #[repr(C)] -pub struct Vertex2D { +pub struct ColoredVertex2D {      /// The vertex position in 2D space.      pub position: [f32; 2], + +    /// The color of the vertex in __linear__ RGBA. +    pub color: color::Packed,  } -/// A two-dimensional vertex with a color. -#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)] +/// A vertex which contains 2D position & packed gradient data. +#[derive(Copy, Clone, Debug, PartialEq)]  #[repr(C)] -pub struct ColoredVertex2D { +pub struct GradientVertex2D {      /// The vertex position in 2D space.      pub position: [f32; 2], -    /// The color of the vertex in __linear__ RGBA. -    pub color: [f32; 4], +    /// The packed vertex data of the gradient. +    pub gradient: gradient::Packed,  } +#[allow(unsafe_code)] +unsafe impl Zeroable for GradientVertex2D {} + +#[allow(unsafe_code)] +unsafe impl Pod for GradientVertex2D {} +  impl From<()> for Primitive {      fn from(_: ()) -> Self {          Self::Group { primitives: vec![] } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index aaf1737a..4241d45c 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,5 +1,5 @@  //! Create a renderer from a [`Backend`]. -use crate::backend::{self, Backend}; +use crate::backend;  use crate::Primitive;  use iced_core::image; @@ -16,13 +16,13 @@ use std::marker::PhantomData;  /// A backend-agnostic renderer that supports all the built-in widgets.  #[derive(Debug)] -pub struct Renderer<B: Backend, Theme> { +pub struct Renderer<B, Theme> {      backend: B,      primitives: Vec<Primitive>,      theme: PhantomData<Theme>,  } -impl<B: Backend, T> Renderer<B, T> { +impl<B, T> Renderer<B, T> {      /// Creates a new [`Renderer`] from the given [`Backend`].      pub fn new(backend: B) -> Self {          Self { @@ -52,10 +52,7 @@ impl<B: Backend, T> Renderer<B, T> {      }  } -impl<B, T> iced_core::Renderer for Renderer<B, T> -where -    B: Backend, -{ +impl<B, T> iced_core::Renderer for Renderer<B, T> {      type Theme = T;      fn layout<Message>( @@ -63,11 +60,7 @@ where          element: &Element<'_, Message, Self>,          limits: &layout::Limits,      ) -> layout::Node { -        let layout = element.as_widget().layout(self, limits); - -        self.backend.trim_measurements(); - -        layout +        element.as_widget().layout(self, limits)      }      fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { @@ -116,7 +109,7 @@ where  impl<B, T> text::Renderer for Renderer<B, T>  where -    B: Backend + backend::Text, +    B: backend::Text,  {      type Font = Font; @@ -195,7 +188,7 @@ where  impl<B, T> image::Renderer for Renderer<B, T>  where -    B: Backend + backend::Image, +    B: backend::Image,  {      type Handle = image::Handle; @@ -210,7 +203,7 @@ where  impl<B, T> svg::Renderer for Renderer<B, T>  where -    B: Backend + backend::Svg, +    B: backend::Svg,  {      fn dimensions(&self, handle: &svg::Handle) -> Size<u32> {          self.backend().viewport_dimensions(handle) @@ -231,13 +224,8 @@ where  }  #[cfg(feature = "geometry")] -impl<B, T> crate::geometry::Renderer for Renderer<B, T> -where -    B: Backend, -{ -    type Geometry = crate::Geometry; - -    fn draw(&mut self, layers: Vec<Self::Geometry>) { +impl<B, T> crate::geometry::Renderer for Renderer<B, T> { +    fn draw(&mut self, layers: Vec<crate::Geometry>) {          self.primitives              .extend(layers.into_iter().map(crate::Geometry::into));      } diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs deleted file mode 100644 index 09b61767..00000000 --- a/graphics/src/triangle.rs +++ /dev/null @@ -1 +0,0 @@ -//! Draw geometry using meshes of triangles. | 
