vsrg 0.1.0

Data structures for vertical scrolling rhythm games
Documentation
//! Relationships between notes, expressed as [`crate::notes::NotesStorageEditHook`]s.
//!
//! # Remarks
//! All hooks are implementations of [`crate::notes::NotesStorageEditHook`] that maintains
//! derived state when the notes storage is edited.
//!
//! The provided relationships cover the common cases:
//!
//! - [`same_start_time::SameStartTime`]: notes starting at (nearly) the same time form a chords
//!   (e.g. highlighting note chord, drawing lines between notes of a chord, common in rhythm games).
//! - [`matching_time_endpoint::MatchingTimeEndpoint`]: notes with matching start/start,
//!   start/end, or end/end times (e.g. Connecting holds' ending to notes with the same time, in games
//!   like Project Sekai).
//! - [`parent_child::ParentChild`]: a note is linked to a "parent" note (e.g. trace note & sky note in
//!   Arcaea).
//! - [`chain_forward::ChainForward`] and [`chain_backward::ChainBackward`]: consecutive notes linked
//!   along the time axis (e.g. arcs chain in Arcaea, streams, jacks, etc.).

mod chain_backward;
mod chain_forward;
mod matching_time_endpoint;
pub mod parent_child;
mod same_start_time;

#[cfg(test)]
mod test_common;

pub use chain_backward::ChainBackward;
pub use chain_forward::ChainForward;
pub use matching_time_endpoint::MatchingTimeEndpoint;
pub use parent_child::ParentChild;
pub use same_start_time::SameStartTime;

use crate::notes::NotesStorage;

/// Symmetric pair predicate over two owned stored-note snapshots, including runtime state.
///
/// # Requirements
/// - The condition must be *commutative*: pair(a, b) = pair(b, a).
/// - The condition must be *transitive*: if pair(a, b) && pair(b, c), then pair (a, c).
pub type PairCondition<S> =
    fn(&<S as NotesStorage>::NoteSnapshotEnum, &<S as NotesStorage>::NoteSnapshotEnum) -> bool;

/// Assign one partner to a note.
///
/// # Remarks
/// The note can copy any data from the partner note as needed. For example, in cases where you
/// need to draw a connection to the partner note, then caching the partner's position in the
/// note's runtime state may be a good idea.
///
/// # Requirements
/// - This operation must be *stateless*: how it writes should not depend on the current runtime
///   state of the note.
/// - This operation must be *idempotent*: multiple applications of this callback on the same note
///   is the same as only one application.
///
/// Effectively, it should sets the partner rather than appending the partner to the note.
pub type AssignParterToNote<S> =
    for<'a> fn(<S as NotesStorage>::NoteMutEnum<'a>, <S as NotesStorage>::NoteSnapshotEnum);

/// Assign multiple partners to a note.
///
/// # Remarks
/// The note can copy any data from the partner notes as needed. For example, in cases where you
/// need to draw a connection to the partner notes, then caching the partner's position in the
/// note's runtime state may be a good idea.
///
/// The slice contains the note's current direct matches, excluding the note itself. Empty slice
/// means deactivating the relationship or unlinking the note from their partners.
///
/// # Requirements
/// - This operation must be *stateless*: how it writes should not depend on the current runtime
///   state of the note.
/// - This operation must be *idempotent*: multiple applications of this callback on the same note
///   is the same as only one application.
///
/// Effectively, it should sets the partners rather than appending the partners to the note.
pub type AssignPartnersToNote<S> =
    for<'a> fn(<S as NotesStorage>::NoteMutEnum<'a>, &[<S as NotesStorage>::NoteSnapshotEnum]);

/// Add a related set of notes to the notes storage.
///
/// # Remarks
/// The notes storage itself generated by [`crate::generate_notes_storage`] allows storing
/// additional user-provided fields. For use cases where relationship state does not belong to any
/// single note (e.g A connected line between notes of the same timing), then it may be stored in
/// such user-provided field instead.
///
/// # Requirements
/// - When called with a set of notes, it should override any previously stored subset of the
///   provided set of notes. For example: if it currently stores {(A, B, C), (D, E), (A, D)}, and
///   the callback is called with (A, B, C, F), then it should now store {(A, B, C, F), (D, E),
///   (A, D)}
/// - This means that the operation must be *idempotent*, since calling with a set that's already
///   stored should override the stored set.
pub type AddRelatedSet<S> = fn(&mut S, &[<S as NotesStorage>::NoteSnapshotEnum]);

/// Remove notes from all related sets in the note storage.
///
/// # Remarks
/// The notes storage itself generated by [`crate::generate_notes_storage`] allows storing
/// additional user-provided fields. For use cases where relationship state does not belong to any
/// single note (e.g A connected line between notes of the same timing), then it may be stored in
/// such user-provided field instead.
///
/// # Requirements
/// - When called with a set of notes, it should those notes from all related sets stored in the
///   notes storage. Additionally, you may want to remove sets which now only contains one note.
///   For example: if it currently stores {(A, B, C), (D, E), (A, D)}, and the callback is called
///   with (C, D), then it should now store {(A, B)}
/// - The operation must be *idempotent*: Removing the same notes twice is the same as removing
///   once.
pub type RemoveFromRelatedSets<S> = fn(&mut S, &[<S as NotesStorage>::NoteSnapshotEnum]);

pub type ChainCondition<S> =
    fn(&<S as NotesStorage>::NoteEnum, &<S as NotesStorage>::NoteEnum) -> bool;

pub type LinkStorage<S> =
    fn(&mut S, &<S as NotesStorage>::NoteSnapshotEnum, &[<S as NotesStorage>::NoteSnapshotEnum]);