Skip to main content

harmonica/
lib.rs

1#![forbid(unsafe_code)]
2#![cfg_attr(not(feature = "std"), no_std)]
3// Allow these clippy lints for physics/math code readability
4#![allow(clippy::must_use_candidate)]
5#![allow(clippy::suboptimal_flops)]
6#![allow(clippy::doc_markdown)]
7#![allow(clippy::use_self)]
8#![allow(clippy::return_self_not_must_use)]
9#![allow(clippy::missing_const_for_fn)]
10#![allow(clippy::cast_lossless)]
11#![allow(clippy::struct_field_names)]
12
13//! # Harmonica
14//!
15//! Physics-based animation tools for 2D and 3D applications.
16//!
17//! Harmonica provides:
18//! - **Spring**: A damped harmonic oscillator for smooth, realistic motion
19//! - **Projectile**: A simple projectile simulator for particles and projectiles
20//!
21//! ## Role in `charmed_rust`
22//!
23//! Harmonica is a foundational crate that supplies physics-based motion used across
24//! the ecosystem:
25//! - **bubbletea** uses it for time-based animation helpers.
26//! - **bubbles** uses it to animate components like progress bars.
27//! - **demo_showcase** uses it to demonstrate smooth, springy UI motion.
28//!
29//! ## Spring Example
30//!
31//! ```rust
32//! use harmonica::{fps, Spring};
33//!
34//! // Initialize the spring once
35//! let spring = Spring::new(fps(60), 6.0, 0.2);
36//!
37//! // Update in your animation loop
38//! let mut pos = 0.0;
39//! let mut vel = 0.0;
40//! let target = 100.0;
41//!
42//! // Simulate for 2 seconds (120 frames at 60 FPS)
43//! for _ in 0..120 {
44//!     (pos, vel) = spring.update(pos, vel, target);
45//! }
46//!
47//! // Position should approach target
48//! assert!((pos - target).abs() < 5.0);
49//! ```
50//!
51//! ## Projectile Example
52//!
53//! ```rust
54//! use harmonica::{fps, Point, Vector, Projectile, TERMINAL_GRAVITY};
55//!
56//! // Create a projectile with gravity
57//! let mut projectile = Projectile::new(
58//!     fps(60),
59//!     Point::new(0.0, 0.0, 0.0),
60//!     Vector::new(10.0, -5.0, 0.0),
61//!     TERMINAL_GRAVITY,
62//! );
63//!
64//! // Update each frame
65//! let pos = projectile.update();
66//! ```
67//!
68//! ## Damping Ratios
69//!
70//! The damping ratio determines the spring's behavior:
71//!
72//! - **Over-damped (ζ > 1)**: No oscillation, slow return to equilibrium
73//! - **Critically-damped (ζ = 1)**: Fastest return without oscillation
74//! - **Under-damped (ζ < 1)**: Oscillates around equilibrium with decay
75//!
76//! ## Attribution
77//!
78//! The spring algorithm is based on Ryan Juckett's damped harmonic motion:
79//! <https://www.ryanjuckett.com/damped-springs/>
80
81mod projectile;
82mod spring;
83
84pub use projectile::{GRAVITY, Point, Projectile, TERMINAL_GRAVITY, Vector};
85pub use spring::{Spring, fps};
86
87/// Prelude module for convenient imports.
88pub mod prelude {
89    pub use crate::projectile::{GRAVITY, Point, Projectile, TERMINAL_GRAVITY, Vector};
90    pub use crate::spring::{Spring, fps};
91}