diff --git a/core/src/geom.rs b/core/src/geom.rs index 5ba5b6f4..74e732fe 100644 --- a/core/src/geom.rs +++ b/core/src/geom.rs @@ -1,3 +1,5 @@ +//! Basic geometric primitives. + /// Vertex with position and arbitrary other attributes. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Vertex { @@ -5,7 +7,7 @@ pub struct Vertex { pub attrib: A, } -/// Triangle, consisting of three vertices. +/// Triangle, defined by three vertices. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Tri(pub [V; 3]); diff --git a/core/src/lib.rs b/core/src/lib.rs index 8f834c5c..3bbf8469 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,10 +9,20 @@ //! /_____,´ //! ``` //! +//! Core functionality of the `retrofire` project. +//! +//! Includes a math library with vectors, matrices, colors, and angles; basic +//! geometry primitives; a software 3D renderer with customizable shaders; //! # Features -//! * `std`: Enables `std` support. -//! This makes available items requiring `std::time` or floating-point -//! functions. Disabled by default; only `alloc` is required. +//! * `std`: +//! Makes available items requiring floating-point functions or +//! `std::time`. If disabled, this crate only depends on `alloc`. +//! +//! * `micromath`: +//! Provides an alternative, no-std implementation of floating-point +//! functions via [micromath](https://crates.io/crates/micromath). +//! +//! All features are disabled by default. #![cfg_attr(not(feature = "std"), no_std)] diff --git a/core/src/math.rs b/core/src/math.rs index 1df03d1d..55a61a75 100644 --- a/core/src/math.rs +++ b/core/src/math.rs @@ -1,3 +1,22 @@ +//! Linear algebra and more. +//! +//! Includes [vectors][self::vec], [matrices][mat], [colors][color], and +//! [angles][angle], as well as support for custom "linear" types usable as +//! [varyings][vary] and utilities such as approximate equality comparisons. +//! TODO: Bezier splines and pseudorandom numbers. +//! +//! This library is more strongly typed than many other similar ones. It aims +//! to prevent at compile time many errors that might otherwise only manifest +//! as graphical glitches, runtime panics, or even (particularly in languages +//! that are unsafe-by-default) undefined behavior. +//! +//! In particular, vectors and colors are tagged with a type that represents +//! the *space* they're embedded in, and values in different spaces cannot be +//! mixed without explicit conversion (transformation). Matrices, similarly, +//! are tagged by both source and destination space, and can only be applied +//! to matching vectors. Angles are strongly typed as well, to allow working +//! with different angular units without confusion. + pub use angle::{degs, rads, turns, Angle}; pub use mat::{Mat3x3, Mat4x4, Matrix}; pub use vec::{vec2, vec3, vec4}; diff --git a/core/src/math/angle.rs b/core/src/math/angle.rs index 3ba7098d..83f20ae0 100644 --- a/core/src/math/angle.rs +++ b/core/src/math/angle.rs @@ -95,8 +95,8 @@ impl Angle { /// assert_approx_eq!(degs(400.0).wrap(Angle::ZERO, Angle::FULL), degs(40.0)) /// ``` #[must_use] - pub fn wrap(self, Self(min): Self, Self(max): Self) -> Self { - Self(min + (self.0 - min).rem_euclid(max - min)) + pub fn wrap(self, min: Self, max: Self) -> Self { + Self(min.0 + (self.0 - min.0).rem_euclid(max.0 - min.0)) } } diff --git a/core/src/math/approx.rs b/core/src/math/approx.rs index ea31125b..29155501 100644 --- a/core/src/math/approx.rs +++ b/core/src/math/approx.rs @@ -85,16 +85,16 @@ impl> ApproxEq for Option { /// If the given values are not approximately equal. /// /// # Examples -/// -/// The following assertion passes even though `assert_eq` fails: +/// `assert_eq` would fail, but `assert_approx_eq` passes: /// ``` /// # use retrofire_core::assert_approx_eq; +/// assert_ne!(0.1 + 0.2, 0.3); /// assert_approx_eq!(0.1 + 0.2, 0.3); -/// //assert_eq!(0.1 + 0.2, 0.3); Would panic! /// ``` /// A relative epsilon is used: /// ``` /// # use retrofire_core::assert_approx_eq; +/// assert_ne!(1e7, 1e7 + 1.0); /// assert_approx_eq!(1e7, 1e7 + 1.0); /// ``` /// A custom epsilon can be given: @@ -102,13 +102,13 @@ impl> ApproxEq for Option { /// # use retrofire_core::assert_approx_eq; /// assert_approx_eq!(100.0, 101.0, eps = 0.01); /// ``` -/// Like `assert_eq`, supports custom panic messages. The epsilon is -/// optional but must come before the format string. +/// Like `assert_eq`, this macro supports custom panic messages. +/// The epsilon, if present, must come before the format string. /// ```should_panic /// # use std::f32; /// # use retrofire_core::assert_approx_eq; /// assert_approx_eq!(f32::sin(3.14), 0.0, eps = 0.001, -/// "3.14 is not close enough to {}!", f32::consts::PI); +/// "3.14 is not a good approximation of {}!", f32::consts::PI); /// ``` #[macro_export] macro_rules! assert_approx_eq { diff --git a/core/src/render/raster.rs b/core/src/render/raster.rs index bc7ff842..4f750512 100644 --- a/core/src/render/raster.rs +++ b/core/src/render/raster.rs @@ -142,11 +142,11 @@ where } /// Returns an iterator that emits a scanline for each line from `y0` to `y1`, -/// interpolating fragment values from `l0` to `l1` on the left side and from -/// `r0` to `r1` on the right side. +/// interpolating varyings from `l0` to `l1` on the left and from `r0` to `r1` +/// on the right side. /// -/// The three input ranges define a *trapezoid* with horizontal top and bottom -/// edges, or, in the special case where `l0 == r0` or `l1 == r1`, a triangle: +/// The three input ranges define a *trapezoid* with horizontal bases, or, in +/// the special case where `l0 == r0` or `l1 == r1`, a triangle: /// ```text /// l0___________ r0 /// y0 _|____________| .next() @@ -158,9 +158,9 @@ where /// Any convex polygon can be converted into scanlines by dividing it into /// trapezoidal segments and calling this function for each segment. /// -/// Which exact pixels to draw is determined by whether the vector shape -/// *covers* a pixel or not. A pixel is covered, and drawn, if and only if -/// its center point lies inside the shape. This ensures that if two polygons +/// The exact pixels that are drawn are determined by whether the vector shape +/// *covers* a pixel or not. A pixel is covered, and drawn, if and only if its +/// center point lies inside the shape. This ensures that if two polygons /// share an edge, or several share a vertex, each pixel at the boundary will /// be drawn by exactly one of the polygons, with no gaps or overdrawn pixels. pub fn scan( diff --git a/core/src/util.rs b/core/src/util.rs index 09c3b578..fc5c601c 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -1,2 +1,4 @@ +//! Various utility types and functions. + pub mod buf; pub mod rect; diff --git a/core/src/util/buf.rs b/core/src/util/buf.rs index fc2ddaff..c4dce121 100644 --- a/core/src/util/buf.rs +++ b/core/src/util/buf.rs @@ -1,3 +1,7 @@ +//! Two-dimensional buffers, with owned and borrowed variants. +//! +//! Useful for storing pixel data of any kind, among other things. + use alloc::vec::Vec; use core::fmt::{Debug, Formatter}; use core::iter::repeat; @@ -9,14 +13,14 @@ use inner::Inner; // Traits // -/// A trait for types that can provide a view of their data as a `Slice2` +/// A trait for types that can provide a view of their data as a [`Slice2`]. pub trait AsSlice2 { /// Returns a borrowed `Slice2` view of `Self`. fn as_slice2(&self) -> Slice2; } /// A trait for types that can provide a mutable view of their data -/// as a `MutSlice2` +/// as a [`MutSlice2`]. pub trait AsMutSlice2 { /// Returns a mutably borrowed `MutSlice2` view of `Self`. fn as_mut_slice2(&mut self) -> MutSlice2; @@ -28,8 +32,11 @@ pub trait AsMutSlice2 { /// A rectangular 2D buffer that owns its elements, backed by a `Vec`. /// +/// Unlike `Vec`, however, `Buf2` cannot be resized after construction +/// without explicitly copying the contents to a new, larger buffer. +/// /// `Buf2` stores its elements contiguously, in standard row-major order, -/// such that element (x, y) maps to element at index +/// such that the coordinate pair (x, y) maps to the index /// ```text /// buf.width() * y + x /// ``` @@ -79,8 +86,8 @@ pub struct MutSlice2<'a, T>(Inner); // impl Buf2 { - /// Returns a buffer with size `w` × `h`, with elements initialized - /// with values from `init` in row-major order. + /// Returns a buffer with size `w` × `h`, with elements initialized in + /// row-major order with values yielded by `init`. /// /// # Panics /// If there are fewer than `w * h` elements in `init`. diff --git a/core/src/util/rect.rs b/core/src/util/rect.rs index 1628f11c..48d69d40 100644 --- a/core/src/util/rect.rs +++ b/core/src/util/rect.rs @@ -1,3 +1,5 @@ +//! Rectangular regions; essentially two-dimensional ranges. + use core::fmt::Debug; use core::ops::{Bound::*, RangeBounds, RangeFull, Sub}; @@ -5,9 +7,13 @@ use crate::math::vec::{Real, Vector}; #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Rect { + /// The left bound of `self`, if any. pub left: Option, + /// The top bound of `self`, if any. pub top: Option, + /// The right bound of `self`, if any. pub right: Option, + /// The bottom bound of `self`, if any. pub bottom: Option, } diff --git a/front/src/lib.rs b/front/src/lib.rs index 292e56a5..c61d4ebf 100644 --- a/front/src/lib.rs +++ b/front/src/lib.rs @@ -1,13 +1,22 @@ +//! Frontends for creating simple applications with `retrofire`. + +use std::time::Duration; + +use retrofire_core::render::target::Framebuf; +use retrofire_core::util::buf::MutSlice2; + #[cfg(feature = "minifb")] pub mod minifb; #[cfg(feature = "sdl2")] pub mod sdl2; +/// Per-frame state. [`Window::run`][minifb::Window::run] passes an instance +/// of `Frame` to the callback function on every iteration of the main loop. pub struct Frame<'a> { - // Time since first frame. + /// Time since the first frame. pub t: Duration, - // Time since last frame. + /// Time since the previous frame. pub dt: Duration, // Framebuffer in which to draw. pub buf: Framebuf, MutSlice2<'a, f32>>, diff --git a/front/src/minifb.rs b/front/src/minifb.rs index d39086ed..fcda6372 100644 --- a/front/src/minifb.rs +++ b/front/src/minifb.rs @@ -3,13 +3,14 @@ use std::ops::ControlFlow::{self, Break}; use std::time::{Duration, Instant}; -use minifb::{Key, KeyRepeat, WindowOptions}; +use minifb::{Key, WindowOptions}; use retrofire_core::render::target::Framebuf; use retrofire_core::util::buf::{AsMutSlice2, Buf2}; use crate::Frame; +/// A lightweight wrapper of a `minibuf` window. pub struct Window { /// The wrapped minifb window. pub imp: minifb::Window, @@ -47,7 +48,8 @@ impl<'t> Builder<'t> { self.title = title; self } - /// Sets the fps cap of the window. + /// Sets the frame rate cap of the window. `None` means unlimited + /// frame rate (the main loop runs as fast as possible). pub fn max_fps(mut self, fps: Option) -> Self { self.max_fps = fps; self @@ -84,14 +86,23 @@ impl Window { Builder::default() } - /// Updates the window content with `fb`. + /// Updates the window content with pixel data from `fb`. + /// + /// The data is interpreted as colors in `0x00_RR_GG_BB` format. /// # Panics + /// If `fb.len() < self.size.0 * self.size.1`. pub fn present(&mut self, fb: &[u32]) { let (w, h) = self.size; self.imp.update_with_buffer(fb, w, h).unwrap(); } - /// Repeatedly calls `frame_fn` + /// Runs the main loop of the program, invoking the callback on each + /// iteration to compute and draw the next frame. + /// + /// The main loop stops and this function returns if: + /// * the user closes the window via the GUI (eg. titlebar close button); + /// * the Esc key is pressed; or + /// * the callback returns `ControlFlow::Break`. pub fn run(&mut self, mut frame_fn: F) where F: FnMut(&mut Frame) -> ControlFlow<()>, @@ -103,11 +114,7 @@ impl Window { let start = Instant::now(); let mut last = Instant::now(); loop { - if !self.imp.is_open() - || self - .imp - .is_key_pressed(Key::Escape, KeyRepeat::No) - { + if !self.imp.is_open() || self.imp.is_key_down(Key::Escape) { return; }