Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ivan-1f/phichain/llms.txt

Use this file to discover all available pages before exploring further.

Overview

LineEvents control how judgment lines move, rotate, and change appearance over time. Each event has a type (kind), a time range (start_beat to end_beat), and a value that can either transition smoothly or remain constant.

Types

LineEvent

An animation event that modifies a line property.
kind
LineEventKind
required
Which property this event affects (X, Y, Rotation, Opacity, or Speed)
start_beat
Beat
required
When the event begins taking effect
end_beat
Beat
required
When the event stops taking effect
value
LineEventValue
required
The value(s) for this event - either a transition or constant

LineEventKind

The type of property being animated.
X
u8 = 1
Horizontal position (screen coordinates, 0 = center)
Y
u8 = 2
Vertical position (screen coordinates, 0 = center)
Rotation
u8 = 3
Rotation angle in degrees
Opacity
u8 = 4
Line visibility (0.0 = invisible, 1.0 = fully visible)
Speed
u8 = 5
Note movement speed multiplier

LineEventValue

The value that the event applies to the line property.

Creating Events

use phichain_chart::event::{LineEvent, LineEventKind, LineEventValue};
use phichain_chart::easing::Easing;
use phichain_chart::beat::Beat;

// Move line horizontally from left to right
let x_event = LineEvent {
    kind: LineEventKind::X,
    start_beat: Beat::ZERO,
    end_beat: Beat::from(4.0),
    value: LineEventValue::transition(-0.5, 0.5, Easing::Linear),
};

// Move line vertically with smooth easing
let y_event = LineEvent {
    kind: LineEventKind::Y,
    start_beat: Beat::from(4.0),
    end_beat: Beat::from(8.0),
    value: LineEventValue::transition(0.0, 0.3, Easing::EaseInOutSine),
};

Event Evaluation

Events can be evaluated at any beat to get the current value:
use phichain_chart::event::{LineEvent, EventEvaluationResult};

let event = LineEvent {
    kind: LineEventKind::X,
    start_beat: Beat::ZERO,
    end_beat: Beat::from(4.0),
    value: LineEventValue::transition(0.0, 1.0, Easing::Linear),
};

// Evaluate at different beats
let result = event.evaluate(2.0); // halfway through
match result {
    EventEvaluationResult::Affecting(value) => {
        println!("Current value: {}", value); // 0.5
    }
    _ => {}
}

Helper Methods

use phichain_chart::event::LineEventValue;
use phichain_chart::easing::Easing;

// Create values
let transition = LineEventValue::transition(0.0, 1.0, Easing::Linear);
let constant = LineEventValue::constant(0.5);

// Check type
if transition.is_transition() {
    println!("This is a transition");
}
if constant.is_constant() {
    println!("This is a constant");
}

// Get start/end values
let start = transition.start(); // 0.0
let end = transition.end();     // 1.0

// For constants, start and end are the same
assert_eq!(constant.start(), constant.end());

// Convert between types
let as_constant = transition.into_constant(); // uses start value
let as_transition = constant.into_transition(); // start = end = value

// Negate values
let negated = transition.negated(); // start: 0.0, end: -1.0

JSON Serialization

{
  "kind": "x",
  "start_beat": [0, 0, 1],
  "end_beat": [4, 0, 1],
  "value": {
    "transition": {
      "start": -0.5,
      "end": 0.5,
      "easing": "linear"
    }
  }
}

Event Coordination

use phichain_chart::serialization::SerializedLine;
use phichain_chart::line::Line;

let line = SerializedLine::new(
    Line { name: "Animated Line".to_string() },
    vec![],
    vec![
        // Move horizontally
        LineEvent {
            kind: LineEventKind::X,
            start_beat: Beat::ZERO,
            end_beat: Beat::from(8.0),
            value: LineEventValue::transition(-0.5, 0.5, Easing::Linear),
        },
        // Move vertically at same time
        LineEvent {
            kind: LineEventKind::Y,
            start_beat: Beat::ZERO,
            end_beat: Beat::from(8.0),
            value: LineEventValue::transition(-0.3, 0.3, Easing::Linear),
        },
        // Rotate while moving
        LineEvent {
            kind: LineEventKind::Rotation,
            start_beat: Beat::ZERO,
            end_beat: Beat::from(8.0),
            value: LineEventValue::transition(0.0, 360.0, Easing::Linear),
        },
        // Keep visible
        LineEvent {
            kind: LineEventKind::Opacity,
            start_beat: Beat::ZERO,
            end_beat: Beat::from(8.0),
            value: LineEventValue::constant(1.0),
        },
    ],
    vec![],
    vec![],
);

Notes

  • Events of the same kind can overlap - the game typically uses the most recent active event
  • Speed events affect how fast notes approach the line, not the line’s movement speed
  • Opacity affects both the line and notes on it
  • Position coordinates: (0, 0) is screen center, positive X is right, positive Y is up
  • Rotation is in degrees, clockwise
  • After an event ends, its end value is typically inherited until another event of the same kind begins

Build docs developers (and LLMs) love