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:
minsung
2026-04-14 20:55:16 +09:00
parent 257630f64b
commit 263806b834
6 changed files with 377 additions and 0 deletions

View File

@@ -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.