Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a nannou_core crate. Switch from cgmath to glam. Remove geom::Graph. Remove generic Scalar param from Draw API. #754

Merged
merged 10 commits into from
Jun 20, 2021

Conversation

mitchmindtree
Copy link
Member

@mitchmindtree mitchmindtree commented Jun 18, 2021

This is a large refactoring of nannou's internals aimed at improving modularity, compile times, API documentation, reducing maintenance burden and improving nannou's suitability in headless and embedded applications.

nannou_core

nannou's core abstractions.

This crate aims to be a stripped-down foundation for nannou projects that don't require
windowing or wgpu graphics. These might include:

  • Headless applications, e.g. for LASER or lighting control.
  • Embedded applications, e.g. driving motors or LEDs in an art installation.
  • rust-gpu shaders which have strict requirements beyond the limitations of no_std.
  • Hot-loaded dynamic libraries that benefit from faster compilation times.

The crate includes nannou's color, math, geometry and noise abstractions without the deep stack
of crates required to establish an event loop, interoperate with wgpu, etc. Another way of
describing this crate might be "nannou without the I/O".

Crate [features]

The primary feature of this crate is support for #![no_std]. This means we can use the crate
for embedded applications and in some cases rust-gpu shaders.

By default, the std feature is enabled. For compatibility with a #![no_std] environment be
sure to disable default features (i.e. default-features = false) and enable the libm
feature. The libm feature provides some core functionality required by crates

  • std: Enabled by default, enables the Rust std library. One of the primary features of this
    crate is support for #![no_std]. This means we can use the crate for embedded applications
    and in some cases rust-gpu shaders. For compatibility with a #![no_std] environment be sure
    to disable default features (i.e. default-features = false) and enable the libm feature.
  • libm: provides some core math support in the case that std is not enabled. This feature
    must be enabled if std is disabled.
  • serde: enables the associated serde serialization/deserialization features in glam,
    palette and rand.

Closes #752.
Related to #209, #387, #178.

cgmath -> glam

Previously, nannou's linear algebra foundation was provided by cgmath, however this PR switches to using the glam crate. Motivations for this change include:

  • A simpler API, reminiscent of glm though more rusty.
  • Easier to navigate documentation.
  • Super fast compile times. https://github.com/bitshifter/mathbench-rs#build-times
  • Support for the rust-gpu spirv-unknown-unknown target, i.e. writing shaders in Rust.
  • Support for #![no_std] environments such as embedded, rust-gpu.
  • serde feature for (de)serialization support.
  • rand feature providing rand crate distribution implementations.

As a part of this transition, the nannou::geom::vector module has been removed in favour of leaning on glam's Vec2/3/4 types. For the most part, both APIs are very similar, bar a few key differences:

  • glam's Vec* types are opaque and cannot be constructed directly from its fields. In turn a constructor should be used instead, e.g. Vector2 { x, y } -> vec2(x, y) or [x, y].into().
  • The glam magnitude equivalent method is called length
  • glam does not provide a Vec*::with_length method. Instead, users can use v.normalize() * length to the same effect.

It should be noted that the nalgebra-glm crate was also a close consideration, however after some discussion in the nannou-dev matrix channel and the EmbarkStudios discord I lent towards glam primarily for its much faster compile times and use within the new spirv_std crate as the standard math library for working with rust-gpu shaders.

Related to #387, #178.

Draw<S> becomes Draw

This PR removes the generic scalar type parameter on the Draw API and related items in the hope of both improving compile times and simplifying the API a little. The only use case for this generic parameter was if a user wanted to instantiate their own Draw instance that used f64 as the scalar type, rather than the default f32. However, the draw API's render backend has only ever supported using the default f32 scalar type, so using f64 would also have required that the user write their own rendering backend - a feat that I doubt anyone would have taken on without getting in touch about making the existing backend more generic.

The removal of this type parameter should hopefully make the draw documentation a little easier to grok, and also remove a lot of the previously required monomorphisation around the draw API and in turn improve re-compilation of downstream crates at least a bit. That said, beware I'm yet to setup any proper compile-time bench-marking for nannou.

Removing geom::Graph

The geom::Graph abstraction has been seemingly unused since it was added. Originally I thought it might make for a useful dynamic way of composing more complex geometry together. I never ended up finding a particular use for it, and I feel it's likely even less useful now that the Draw API allows for applying arbitrary transformations (scaling, rotation, translation). Please let me know here if you do use it or foresee yourself using it along with your use-case and we can consider re-adding it.

TODO

  • Update downstream crates and examples for breaking changes.
  • Update tutorials for API changes.
  • Update changelog.
  • Add job to CI for testing #![no_std] build of nannou_core.
  • Add publish step for nannou_core.

Future Work

@mitchmindtree mitchmindtree marked this pull request as ready for review June 18, 2021 21:52
deg_to_rad(v.z),
))
pub fn degrees(&self, v: Vec3) -> Self {
self.radians(vec3(deg_to_rad(v.x), deg_to_rad(v.y), deg_to_rad(v.z)))
Copy link
Contributor

@danwilhelm danwilhelm Jun 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

glam uses the following for their tests, kind of an interesting idea that doesn't involve math:

pub trait Deg {
    fn to_radians(self) -> Self;
}
      
impl Deg for f32 {
    fn to_radians(self) -> f32 {
        f32::to_radians(self)
    }
}

impl Deg for f64 {
    fn to_radians(self) -> f64 {
        f32::to_radians(self)
    }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's a pity they don't expose these from the crate! I guess the tricky thing is that using traits like this would begin to conflict if you had more than 2, e.g. if trait Deg and trait Turns both declare a to_radians method and both are implemented for f32, you'd need to distinguish which you're talking about at the call-site with Deg::to_radians(f) rather than being able to do f.to_radians(). Maybe that's not too bad though.

I did add a ConvertAngle trait to the math mod which provides these functions as methods, so you could do f.deg_to_rad(), f.rad_to_turns(), etc, but I wasn't sure if it was worth adding it to the prelude if we already have these in the prelude as functions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True! Feel free to resolve, this was moreso just a remark 😂 It's really difficult to find anything out of ordinary in your revisions!

See nannou-org#752 for motivation behind adding a `nannou_core` crate.

Note that this commit does not yet replace the color, geom, math and
rand modules within the `nannou` crate. This will be performed in a
following commit.
This removes the color, math, rand and geom modules in favour of
depending on the `nannou_core` crate. The `nannou_core` modules are
re-exported within `nannou` in order to provide a near identical API to
the existing one.

The majority of changes here revolve around the change from `cgmath` to
`glam`. The most notable changes include:

- `geom::Vector2` -> `glam::Vec2`
- `cgmath::Matrix4<f32>` -> `glam::Mat4`
- `magnitude` -> `length`

This change also removes the generic `Scalar` type parameter from the
`Draw` API and related items in the hope of improving downstream compile
times.

Also removes the seemingly unused `geom::graph` module. Use-cases for
this module and its abstractions were mostly made reduntatnt upon
allowing for transformations to be applied to the `Draw` instance
itself.
This refactors the `Rect` and `Cuboid` methods to try to use `Point*`
and `Vec*` types where possible for `Rect<f32>` and `Rect<f64>` in order
to maintain an API closer to the original and avoid large sweeps amounts
of breaking changes.

Also exposes common `glam` types via the `nannou_core` `prelude` module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Abstract a nannou_core crate for use in no_std or headless environments
2 participants