Sprint 11/12/13 — 선택하이라이트 + 저장/로드 + Tauri앱 스켈레톤
Sprint 11 (Selection highlight + 단면 UI): - FeatureDraw: CPU 정점 저장, update_highlight() — 선택 시 yellow-orange - 렌더 루프: background mesh(지면+선형) + 피처별 독립 draw call 분리 - SceneParams: GirderSectionType (PscI / SteelBox), show_alignment - egui: 단면형식 ComboBox, 선형표시 checkbox - SteelBox 단면 지원 (span 비례 자동 치수) - build_background_scene(): 지면+선형만 반환 Sprint 12 (Project save/load): - project_file.rs: ProjectFile struct, to_params/from_params, save/load JSON - egui: 💾 저장 / 📂 불러오기 버튼 - projects/ 폴더 자동 생성 Sprint 13 (Tauri app skeleton): - crates/app/: Cargo.toml + main.rs (Tauri v2 통합 scaffold) - 기동 시 PureRustKernel 동작 검증 - Tauri setup checklist 주석으로 문서화 - workspace에 cimery-app 추가 cargo check --workspace 통과 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
79
cimery/crates/viewer/src/project_file.rs
Normal file
79
cimery/crates/viewer/src/project_file.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
//! cimery project file — JSON save/load of SceneParams.
|
||||
//!
|
||||
//! Format: `cimery/projects/*.cimery.json`
|
||||
//! Sprint 12: SceneParams only. Sprint 13+: Feature IRs + Alignment.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::bridge_scene::{GirderSectionType, SceneParams};
|
||||
|
||||
// ─── Serialisable form of SceneParams ────────────────────────────────────────
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SectionTypeStr(String);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ProjectFile {
|
||||
pub version: u32,
|
||||
pub name: String,
|
||||
pub span_m: f64,
|
||||
pub girder_count: usize,
|
||||
pub girder_spacing: f32,
|
||||
pub girder_height: f32,
|
||||
pub slab_thickness: f32,
|
||||
pub section_type: String, // "psc_i" | "steel_box"
|
||||
pub show_alignment: bool,
|
||||
}
|
||||
|
||||
impl ProjectFile {
|
||||
pub fn from_params(name: &str, p: &SceneParams) -> Self {
|
||||
Self {
|
||||
version: 1,
|
||||
name: name.to_owned(),
|
||||
span_m: p.span_m,
|
||||
girder_count: p.girder_count,
|
||||
girder_spacing: p.girder_spacing,
|
||||
girder_height: p.girder_height,
|
||||
slab_thickness: p.slab_thickness,
|
||||
section_type: match p.section_type {
|
||||
GirderSectionType::PscI => "psc_i".into(),
|
||||
GirderSectionType::SteelBox => "steel_box".into(),
|
||||
},
|
||||
show_alignment: p.show_alignment,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_params(&self) -> SceneParams {
|
||||
SceneParams {
|
||||
span_m: self.span_m,
|
||||
girder_count: self.girder_count,
|
||||
girder_spacing: self.girder_spacing,
|
||||
girder_height: self.girder_height,
|
||||
slab_thickness: self.slab_thickness,
|
||||
section_type: match self.section_type.as_str() {
|
||||
"steel_box" => GirderSectionType::SteelBox,
|
||||
_ => GirderSectionType::PscI,
|
||||
},
|
||||
show_alignment: self.show_alignment,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&self, path: &std::path::Path) -> std::io::Result<()> {
|
||||
let json = serde_json::to_string_pretty(self)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
std::fs::write(path, json)
|
||||
}
|
||||
|
||||
pub fn load(path: &std::path::Path) -> std::io::Result<Self> {
|
||||
let json = std::fs::read_to_string(path)?;
|
||||
serde_json::from_str(&json)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Default project save path (relative to cimery workspace root).
|
||||
pub fn default_save_path(name: &str) -> std::path::PathBuf {
|
||||
let mut p = std::path::PathBuf::from("projects");
|
||||
std::fs::create_dir_all(&p).ok();
|
||||
p.push(format!("{}.cimery.json", name));
|
||||
p
|
||||
}
|
||||
Reference in New Issue
Block a user