//! Draw and generate geometry.
use crate::core::{Point, Radians, Rectangle, Size, Vector};
use crate::geometry::{self, Fill, Path, Stroke, Text};
/// The region of a surface that can be used to draw geometry.
#[allow(missing_debug_implementations)]
pub struct Frame<Renderer>
where
Renderer: geometry::Renderer,
{
raw: Renderer::Frame,
}
impl<Renderer> Frame<Renderer>
where
Renderer: geometry::Renderer,
{
/// Creates a new [`Frame`] with the given dimensions.
pub fn new(renderer: &Renderer, size: Size) -> Self {
Self {
raw: renderer.new_frame(size),
}
}
/// Returns the width of the [`Frame`].
pub fn width(&self) -> f32 {
self.raw.width()
}
/// Returns the height of the [`Frame`].
pub fn height(&self) -> f32 {
self.raw.height()
}
/// Returns the dimensions of the [`Frame`].
pub fn size(&self) -> Size {
self.raw.size()
}
/// Returns the coordinate of the center of the [`Frame`].
pub fn center(&self) -> Point {
self.raw.center()
}
/// Draws the given [`Path`] on the [`Frame`] by filling it with the
/// provided style.
pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
self.raw.fill(path, fill);
}
/// Draws an axis-aligned rectangle given its top-left corner coordinate and
/// its `Size` on the [`Frame`] by filling it with the provided style.
pub fn fill_rectangle(
&mut self,
top_left: Point,
size: Size,
fill: impl Into<Fill>,
) {
self.raw.fill_rectangle(top_left, size, fill);
}
/// Draws the stroke of the given [`Path`] on the [`Frame`] with the
/// provided style.
pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
self.raw.stroke(path, stroke);
}
/// Draws the characters of the given [`Text`] on the [`Frame`], filling
/// them with the given color.
///
/// __Warning:__ All text will be rendered on top of all the layers of
/// a `Canvas`. Therefore, it is currently only meant to be used for
/// overlays, which is the most common use case.
pub fn fill_text(&mut self, text: impl Into<Text>) {
self.raw.fill_text(text);
}
/// 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.
#[inline]
pub fn with_save<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
self.push_transform();
let result = f(self);
self.pop_transform();
result
}
/// Pushes the current transform in the transform stack.
pub fn push_transform(&mut self) {
self.raw.push_transform();
}
/// Pops a transform from the transform stack and sets it as the current transform.
pub fn pop_transform(&mut self) {
self.raw.pop_transform();
}
/// Executes the given drawing operations within a [`Rectangle`] region,
/// clipping any geometry that overflows its bounds. Any transformations
/// performed are local to the provided closure.
///
/// This method is useful to perform drawing operations that need to be
/// clipped.
#[inline]
pub fn with_clip<R>(
&mut self,
region: Rectangle,
f: impl FnOnce(&mut Self) -> R,
) -> R {
let mut frame = self.draft(region);
let result = f(&mut frame);
self.paste(frame, Point::new(region.x, region.y));
result
}
/// Creates a new [`Frame`] with the given [`Size`].
///
/// Draw its contents back to this [`Frame`] with [`paste`].
///
/// [`paste`]: Self::paste
fn draft(&mut self, clip_bounds: Rectangle) -> Self {
Self {
raw: self.raw.draft(clip_bounds),
}
}
/// Draws the contents of the given [`Frame`] with origin at the given [`Point`].
fn paste(&mut self, frame: Self, at: Point) {
self.raw.paste(frame.raw, at);
}
/// Applies a translation to the current transform of the [`Frame`].
pub fn translate(&mut self, translation: Vector) {
self.raw.translate(translation);
}
/// Applies a rotation in radians to the current transform of the [`Frame`].
pub fn rotate(&mut self, angle: impl Into<Radians>) {
self.raw.rotate(angle);
}
/// Applies a uniform scaling to the current transform of the [`Frame`].
pub fn scale(&mut self, scale: impl Into<f32>) {
self.raw.scale(scale);
}
/// Applies a non-uniform scaling to the current transform of the [`Frame`].
pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
self.raw.scale_nonuniform(scale);
}
/// Turns the [`Frame`] into its underlying geometry.
pub fn into_geometry(self) -> Renderer::Geometry {
self.raw.into_geometry()
}
}
/// The internal implementation of a [`Frame`].
///
/// Analogous to [`Frame`]. See [`Frame`] for the documentation
/// of each method.
#[allow(missing_docs)]
pub trait Backend: Sized {
type Geometry;
fn width(&self) -> f32;
fn height(&self) -> f32;
fn size(&self) -> Size;
fn center(&self) -> Point;
fn push_transform(&mut self);
fn pop_transform(&mut self);
fn translate(&mut self, translation: Vector);
fn rotate(&mut self, angle: impl Into<Radians>);
fn scale(&mut self, scale: impl Into<f32>);
fn scale_nonuniform(&mut self, scale: impl Into<Vector>);
fn draft(&mut self, clip_bounds: Rectangle) -> Self;
fn paste(&mut self, frame: Self, at: Point);
fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>);
fn fill(&mut self, path: &Path, fill: impl Into<Fill>);
fn fill_text(&mut self, text: impl Into<Text>);
fn fill_rectangle(
&mut self,
top_left: Point,
size: Size,
fill: impl Into<Fill>,
);
fn into_geometry(self) -> Self::Geometry;
}
#[cfg(debug_assertions)]
impl Backend for () {
type Geometry = ();
fn width(&self) -> f32 {
0.0
}
fn height(&self) -> f32 {
0.0
}
fn size(&self) -> Size {
Size::ZERO
}
fn center(&self) -> Point {
Point::ORIGIN
}
fn push_transform(&mut self) {}
fn pop_transform(&mut self) {}
fn translate(&mut self, _translation: Vector) {}
fn rotate(&mut self, _angle: impl Into<Radians>) {}
fn scale(&mut self, _scale: impl Into<f32>) {}
fn scale_nonuniform(&mut self, _scale: impl Into<Vector>) {}
fn draft(&mut self, _clip_bounds: Rectangle) -> Self {}
fn paste(&mut self, _frame: Self, _at: Point) {}
fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into<Stroke<'a>>) {}
fn fill(&mut self, _path: &Path, _fill: impl Into<Fill>) {}
fn fill_text(&mut self, _text: impl Into<Text>) {}
fn fill_rectangle(
&mut self,
_top_left: Point,
_size: Size,
_fill: impl Into<Fill>,
) {
}
fn into_geometry(self) -> Self::Geometry {}
}