elevator_core/components/line.rs
1//! Line (physical path) component — shaft, tether, track, etc.
2
3use serde::{Deserialize, Serialize};
4
5use crate::ids::GroupId;
6
7/// Physical orientation of a line.
8///
9/// This is metadata for external systems (rendering, spatial queries).
10/// The simulation always operates along a 1D axis regardless of orientation.
11#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
12#[non_exhaustive]
13pub enum Orientation {
14 /// Standard vertical elevator shaft.
15 #[default]
16 Vertical,
17 /// Angled incline (e.g., funicular).
18 Angled {
19 /// Angle from horizontal in degrees (0 = horizontal, 90 = vertical).
20 degrees: f64,
21 },
22 /// Horizontal people-mover or transit line.
23 Horizontal,
24}
25
26/// 2D position on a floor plan (for spatial queries and rendering).
27#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
28pub struct FloorPosition {
29 /// X coordinate on the floor plan.
30 pub x: f64,
31 /// Y coordinate on the floor plan.
32 pub y: f64,
33}
34
35/// Component for a line entity — the physical path an elevator car travels.
36///
37/// In a building this is a hoistway/shaft. For a space elevator it is a
38/// tether or cable. The term "line" is domain-neutral.
39///
40/// A line belongs to exactly one [`GroupId`] at a time but can be
41/// reassigned at runtime (swing-car pattern). Multiple cars may share
42/// a line (multi-car shafts); collision avoidance is left to game hooks.
43///
44/// Intrinsic properties only — relationship data (which elevators, which
45/// stops) lives in [`LineInfo`](crate::dispatch::LineInfo) on the
46/// [`ElevatorGroup`](crate::dispatch::ElevatorGroup).
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct Line {
49 /// Human-readable name.
50 pub(crate) name: String,
51 /// Dispatch group this line currently belongs to.
52 pub(crate) group: GroupId,
53 /// Physical orientation (metadata for rendering).
54 pub(crate) orientation: Orientation,
55 /// Optional floor-plan position (for spatial queries).
56 pub(crate) position: Option<FloorPosition>,
57 /// Lowest reachable position along the line axis.
58 pub(crate) min_position: f64,
59 /// Highest reachable position along the line axis.
60 pub(crate) max_position: f64,
61 /// Maximum number of cars allowed on this line (None = unlimited).
62 pub(crate) max_cars: Option<usize>,
63}
64
65impl Line {
66 /// Human-readable name.
67 #[must_use]
68 pub fn name(&self) -> &str {
69 &self.name
70 }
71
72 /// Dispatch group this line currently belongs to.
73 #[must_use]
74 pub const fn group(&self) -> GroupId {
75 self.group
76 }
77
78 /// Physical orientation.
79 #[must_use]
80 pub const fn orientation(&self) -> Orientation {
81 self.orientation
82 }
83
84 /// Optional floor-plan position.
85 #[must_use]
86 pub const fn position(&self) -> Option<&FloorPosition> {
87 self.position.as_ref()
88 }
89
90 /// Lowest reachable position along the line axis.
91 #[must_use]
92 pub const fn min_position(&self) -> f64 {
93 self.min_position
94 }
95
96 /// Highest reachable position along the line axis.
97 #[must_use]
98 pub const fn max_position(&self) -> f64 {
99 self.max_position
100 }
101
102 /// Maximum number of cars allowed on this line.
103 #[must_use]
104 pub const fn max_cars(&self) -> Option<usize> {
105 self.max_cars
106 }
107}