Sprint 6/7/8 — Alignment 로더 + CSV 라운드트립 + IncrementalDb 스캐폴드
Sprint 6 (Alignment JSON 로더): - AlignmentIR: from_file(), position_at(station), total_length_m() - AlignmentStation, AlignmentSpecs in ir crate - alignments/BR-001-test.json: 40m 직선 테스트 선형 Sprint 7 (CSV 라운드트립): - csv_template.rs: girder_params() 레지스트리 - girder_to_csv_template(): 헤더+기본값 CSV 출력 - girder_from_csv(): CSV → Vec<GirderIR> 파싱 - 테스트 3개 (template, multi-row, invalid span) Sprint 8 (IncrementalDb 스캐폴드): - incremental_scene.rs: IncrementalBridge<K> - 안정적 girder ID (슬롯 기반), DB 캐시 → X-translate - Sprint 9에서 전체 Feature IncrementalDb 통합 예정 cargo test 60개 전부 통과 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -138,6 +138,83 @@ pub struct SteelPlateIParams {
|
||||
pub web_thickness: f64,
|
||||
}
|
||||
|
||||
// ─── Alignment IR ────────────────────────────────────────────────────────────
|
||||
|
||||
/// Single station point along an alignment.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AlignmentStation {
|
||||
/// Station distance along alignment [m].
|
||||
pub station: f64,
|
||||
/// X coordinate [mm].
|
||||
pub x: f64,
|
||||
/// Y coordinate [mm].
|
||||
pub y: f64,
|
||||
/// Z coordinate [mm] — elevation.
|
||||
pub z: f64,
|
||||
}
|
||||
|
||||
/// Road/bridge alignment specs.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct AlignmentSpecs {
|
||||
#[serde(default)]
|
||||
pub r#type: String,
|
||||
#[serde(default)]
|
||||
pub design_speed: u32, // km/h
|
||||
#[serde(default)]
|
||||
pub lanes: u32,
|
||||
#[serde(default)]
|
||||
pub lane_width_mm: u32,
|
||||
}
|
||||
|
||||
/// Alignment IR — parsed from cimery's own JSON format (ADR-002 R).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AlignmentIR {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
#[serde(default)]
|
||||
pub coordinate_system: String,
|
||||
pub stations: Vec<AlignmentStation>,
|
||||
#[serde(default)]
|
||||
pub specs: AlignmentSpecs,
|
||||
}
|
||||
|
||||
impl AlignmentIR {
|
||||
/// Load from a cimery alignment JSON file.
|
||||
pub fn from_file(path: impl AsRef<std::path::Path>) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let s = std::fs::read_to_string(path)?;
|
||||
Ok(serde_json::from_str(&s)?)
|
||||
}
|
||||
|
||||
/// Total length in metres (last station - first station).
|
||||
pub fn total_length_m(&self) -> f64 {
|
||||
match (self.stations.first(), self.stations.last()) {
|
||||
(Some(f), Some(l)) => l.station - f.station,
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpolate 3D position at a given station [m].
|
||||
pub fn position_at(&self, station_m: f64) -> Option<[f64; 3]> {
|
||||
if self.stations.len() < 2 { return None; }
|
||||
let pts = &self.stations;
|
||||
// Find surrounding segment
|
||||
for i in 0..pts.len() - 1 {
|
||||
let a = &pts[i];
|
||||
let b = &pts[i + 1];
|
||||
if station_m >= a.station && station_m <= b.station {
|
||||
let t = (station_m - a.station) / (b.station - a.station);
|
||||
return Some([
|
||||
a.x + t * (b.x - a.x),
|
||||
a.y + t * (b.y - a.y),
|
||||
a.z + t * (b.z - a.z),
|
||||
]);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Deck Slab IR ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// Fully-resolved Deck Slab (바닥판) specification.
|
||||
|
||||
Reference in New Issue
Block a user