Sprint 25/26 — 단면 분기 수정 + 다경간 + 피어 배치
Sprint 25: build_selectable_scene 의 SectionType::PscI 하드코딩 제거. - p.section_type 에 따라 PscI / SteelBox 분기 (build_bridge_scene 과 동일 로직). - 사용자가 단면 형식 전환 시 정상 반영. Sprint 26: 다경간 + 교각 배치 (ParaWiki 교각 wiki Phase 1 MVP). - SceneParams: span_count (1~5) + pier_type (SingleColumn/MultiColumn) 추가. - span_m 의미 변경: 전체 교량 길이 → 경간당 길이. total = span_m × span_count. - pier_ir_for_params() helper: 기본 사각 기둥·2m CSB·2.5m TB·5m column_height. - build_bridge_scene / build_selectable_scene: · 거더: 경간마다 독립 세트 (span_count × girder_count 개) · 데크 슬래브: 전 구간 연속 1개 · 받침: 모든 지점 (교대 2 + 교각 span_count-1) · 교각: 내부 지점 span_count-1 개 (새 기능) · 교대: 양 끝 (z=-800, z=total_mm) · 가로보: 경간마다 반복 · 신축이음: 모든 지점 (선택적) - build_background_scene + scene_extents: total_mm 기준으로 ground·alignment 길이 확장. - project_file: version=2, span_count·pier_type 필드 추가 (v1 호환 default 값). - UI ribbon: "경간 수" 슬라이더 + "교각 형식" T/π 선택. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1
cimery/crates/app/gen/schemas/acl-manifests.json
Normal file
1
cimery/crates/app/gen/schemas/acl-manifests.json
Normal file
File diff suppressed because one or more lines are too long
1
cimery/crates/app/gen/schemas/capabilities.json
Normal file
1
cimery/crates/app/gen/schemas/capabilities.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"default":{"identifier":"default","description":"Default cimery app capabilities","local":true,"windows":["main"],"permissions":["core:default","dialog:allow-open","dialog:allow-save","dialog:allow-message"]}}
|
||||||
2310
cimery/crates/app/gen/schemas/desktop-schema.json
Normal file
2310
cimery/crates/app/gen/schemas/desktop-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
2310
cimery/crates/app/gen/schemas/windows-schema.json
Normal file
2310
cimery/crates/app/gen/schemas/windows-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,13 @@
|
|||||||
//! Positions are in the same coordinate space as the girder mesh:
|
//! Positions are in the same coordinate space as the girder mesh:
|
||||||
//! X = transverse (right = +), Y = vertical (up = +), Z = along span.
|
//! X = transverse (right = +), Y = vertical (up = +), Z = along span.
|
||||||
|
|
||||||
use cimery_core::{AbutmentType, BearingType, CrossBeamSection, ExpansionJointType, MaterialGrade, SectionType};
|
use cimery_core::{
|
||||||
|
AbutmentType, BearingType, ColumnShape, CrossBeamSection, ExpansionJointType,
|
||||||
|
MaterialGrade, PierType, SectionType,
|
||||||
|
};
|
||||||
use cimery_ir::{
|
use cimery_ir::{
|
||||||
AbutmentIR, BearingIR, CrossBeamIR, DeckSlabIR, ExpansionJointIR, FeatureId, GirderIR,
|
AbutmentIR, BearingIR, CapBeamIR, CrossBeamIR, DeckSlabIR, ExpansionJointIR, FeatureId,
|
||||||
PscISectionParams, SectionParams, WingWallIR,
|
GirderIR, PierIR, PscISectionParams, SectionParams, WingWallIR,
|
||||||
};
|
};
|
||||||
use cimery_kernel::{GeomKernel, KernelError, Mesh};
|
use cimery_kernel::{GeomKernel, KernelError, Mesh};
|
||||||
|
|
||||||
@@ -26,8 +29,13 @@ pub enum GirderSectionType { PscI, SteelBox }
|
|||||||
/// Changing any field and calling `build_bridge_scene` regenerates the mesh.
|
/// Changing any field and calling `build_bridge_scene` regenerates the mesh.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SceneParams {
|
pub struct SceneParams {
|
||||||
/// Girder span [m]. Range 20–80 m.
|
/// Length of ONE span [m]. Range 20–80 m. Sprint 26 의미 변경: 경간 = 교각·교대
|
||||||
|
/// 간 한 구간의 길이 (전체 교량 길이 = span_m × span_count).
|
||||||
pub span_m: f64,
|
pub span_m: f64,
|
||||||
|
/// 경간 수 (Sprint 26). 1 = 단경간(피어 없음), 2+ = 다경간(내부에 피어 배치).
|
||||||
|
pub span_count: usize,
|
||||||
|
/// 교각 형식 (Sprint 26). SingleColumn=T형, MultiColumn=π형.
|
||||||
|
pub pier_type: PierType,
|
||||||
/// Number of girders (3–6).
|
/// Number of girders (3–6).
|
||||||
pub girder_count: usize,
|
pub girder_count: usize,
|
||||||
/// Girder centre-to-centre spacing [mm].
|
/// Girder centre-to-centre spacing [mm].
|
||||||
@@ -52,6 +60,8 @@ impl Default for SceneParams {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
span_m: 40.0,
|
span_m: 40.0,
|
||||||
|
span_count: 1,
|
||||||
|
pier_type: PierType::SingleColumn,
|
||||||
girder_count: 5,
|
girder_count: 5,
|
||||||
girder_spacing: 2_500.0,
|
girder_spacing: 2_500.0,
|
||||||
girder_height: 1_800.0,
|
girder_height: 1_800.0,
|
||||||
@@ -74,6 +84,48 @@ pub const COL_GROUND: [f32; 3] = [0.35, 0.38, 0.30]; // dark olive ground
|
|||||||
pub const COL_ALIGNMENT: [f32; 3] = [1.00, 0.60, 0.10]; // orange centreline
|
pub const COL_ALIGNMENT: [f32; 3] = [1.00, 0.60, 0.10]; // orange centreline
|
||||||
pub const COL_CROSS_BEAM: [f32; 3] = [0.75, 0.73, 0.65]; // slightly lighter concrete
|
pub const COL_CROSS_BEAM: [f32; 3] = [0.75, 0.73, 0.65]; // slightly lighter concrete
|
||||||
pub const COL_EXP_JOINT: [f32; 3] = [0.20, 0.20, 0.25]; // dark steel
|
pub const COL_EXP_JOINT: [f32; 3] = [0.20, 0.20, 0.25]; // dark steel
|
||||||
|
pub const COL_PIER: [f32; 3] = [0.68, 0.64, 0.55]; // pier concrete (Sprint 26)
|
||||||
|
|
||||||
|
// ─── Pier helper (Sprint 26) ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// 교각 IR 생성 — MVP Phase 1 (wiki 파라미터 카탈로그 30% 입력만 반영).
|
||||||
|
/// 단면 직사각형 고정, CSB/CSL 은 기본값. 컬럼 수는 pier_type 으로 결정.
|
||||||
|
pub fn pier_ir_for_params(
|
||||||
|
station_m: f64,
|
||||||
|
n_girders: usize,
|
||||||
|
spacing_mm: f32,
|
||||||
|
girder_h_mm: f32,
|
||||||
|
pier_type: PierType,
|
||||||
|
) -> PierIR {
|
||||||
|
let n = n_girders as f64;
|
||||||
|
let total_trans = (n - 1.0) * spacing_mm as f64 + 2_000.0;
|
||||||
|
let column_count = match pier_type {
|
||||||
|
PierType::SingleColumn => 1,
|
||||||
|
PierType::MultiColumn => 2,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
let col_spacing = if column_count > 1 { total_trans / 2.0 } else { 0.0 };
|
||||||
|
PierIR {
|
||||||
|
id: FeatureId::new(),
|
||||||
|
station: station_m,
|
||||||
|
skew_angle: 0.0,
|
||||||
|
pier_type,
|
||||||
|
column_shape: ColumnShape::Rectangular,
|
||||||
|
column_count: column_count as u32,
|
||||||
|
column_spacing: col_spacing,
|
||||||
|
column_diameter: 2_000.0, // wiki CSB 매트릭스 (9-13k × ≤15k 영역)
|
||||||
|
column_depth: 2_000.0,
|
||||||
|
column_height: girder_h_mm as f64 + 5_000.0,
|
||||||
|
cap_beam: CapBeamIR {
|
||||||
|
length: total_trans,
|
||||||
|
width: 2_500.0, // wiki TB ≥ 2500
|
||||||
|
depth: 1_500.0,
|
||||||
|
cantilever_left: 800.0,
|
||||||
|
cantilever_right: 800.0,
|
||||||
|
},
|
||||||
|
material: MaterialGrade::C40,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -93,9 +145,13 @@ fn merge(meshes: Vec<Mesh>) -> Mesh {
|
|||||||
// ─── Scene builder ────────────────────────────────────────────────────────────
|
// ─── Scene builder ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Build a complete bridge scene mesh using the provided kernel and parameters.
|
/// Build a complete bridge scene mesh using the provided kernel and parameters.
|
||||||
|
/// Sprint 26: 다경간 + 피어 배치 지원 (span_count 로 제어).
|
||||||
pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<Mesh, KernelError> {
|
pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<Mesh, KernelError> {
|
||||||
let span_m = p.span_m;
|
let span_m = p.span_m;
|
||||||
let span_mm = (p.span_m * 1_000.0) as f32;
|
let span_mm = (p.span_m * 1_000.0) as f32;
|
||||||
|
let span_count = p.span_count.max(1).min(5);
|
||||||
|
let total_m = span_m * span_count as f64;
|
||||||
|
let total_mm = span_mm * span_count as f32;
|
||||||
let n_girders = p.girder_count.max(1).min(10);
|
let n_girders = p.girder_count.max(1).min(10);
|
||||||
let spacing = p.girder_spacing;
|
let spacing = p.girder_spacing;
|
||||||
let girder_h = p.girder_height;
|
let girder_h = p.girder_height;
|
||||||
@@ -132,32 +188,35 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
GirderSectionType::SteelBox => SectionType::SteelBox,
|
GirderSectionType::SteelBox => SectionType::SteelBox,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Girders ────────────────────────────────────────────────────────────────
|
// ── Girders (경간마다 독립 세트) ───────────────────────────────────────────
|
||||||
for i in 0..n_girders {
|
for s in 0..span_count {
|
||||||
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
let z_base = span_mm * s as f32;
|
||||||
let ir = GirderIR {
|
let s_start = span_m * s as f64;
|
||||||
id: FeatureId::new(),
|
for i in 0..n_girders {
|
||||||
station_start: 0.0,
|
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
||||||
station_end: span_m,
|
let ir = GirderIR {
|
||||||
offset_from_alignment: x as f64,
|
id: FeatureId::new(),
|
||||||
section_type: section_type_enum,
|
station_start: s_start,
|
||||||
section: section_enum.clone(),
|
station_end: s_start + span_m,
|
||||||
count: 1,
|
offset_from_alignment: x as f64,
|
||||||
spacing: 0.0,
|
section_type: section_type_enum,
|
||||||
material: MaterialGrade::C50,
|
section: section_enum.clone(),
|
||||||
};
|
count: 1,
|
||||||
let mut mesh = kernel.girder_mesh(&ir)?;
|
spacing: 0.0,
|
||||||
mesh.recolor(COL_GIRDER);
|
material: MaterialGrade::C50,
|
||||||
parts.push(translate(mesh, x, 0.0, 0.0));
|
};
|
||||||
|
let mut mesh = kernel.girder_mesh(&ir)?;
|
||||||
|
mesh.recolor(COL_GIRDER);
|
||||||
|
parts.push(translate(mesh, x, 0.0, z_base));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Deck Slab ──────────────────────────────────────────────────────────────
|
// ── Deck Slab (전 구간 연속) ───────────────────────────────────────────────
|
||||||
// KDS: min 220 mm, width = (N-1)*spacing + 2 × cantilever
|
let half_width = ((n_girders as f32 - 1.0) * spacing) * 0.5 + 1_000.0;
|
||||||
let half_width = ((n_girders as f32 - 1.0) * spacing) * 0.5 + 1_000.0; // 1 m cantilever
|
|
||||||
let deck_ir = DeckSlabIR {
|
let deck_ir = DeckSlabIR {
|
||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station_start: 0.0,
|
station_start: 0.0,
|
||||||
station_end: span_m,
|
station_end: total_m,
|
||||||
width_left: half_width as f64,
|
width_left: half_width as f64,
|
||||||
width_right: half_width as f64,
|
width_right: half_width as f64,
|
||||||
thickness: p.slab_thickness as f64,
|
thickness: p.slab_thickness as f64,
|
||||||
@@ -169,17 +228,16 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
deck_mesh.recolor(COL_DECK);
|
deck_mesh.recolor(COL_DECK);
|
||||||
parts.push(translate(deck_mesh, 0.0, girder_h + p.slab_thickness, 0.0));
|
parts.push(translate(deck_mesh, 0.0, girder_h + p.slab_thickness, 0.0));
|
||||||
|
|
||||||
// ── Bearings ───────────────────────────────────────────────────────────────
|
// ── Bearings (모든 지점: 교대 2 + 교각 span_count-1) ──────────────────────
|
||||||
// 5 per abutment, one under each girder
|
|
||||||
// plan_length(350mm) = 경간 방향 → half = 175mm 으로 Z 센터링.
|
|
||||||
const BEARING_PLAN_LEN: f32 = 350.0;
|
const BEARING_PLAN_LEN: f32 = 350.0;
|
||||||
const BEARING_PLAN_WID: f32 = 450.0;
|
const BEARING_PLAN_WID: f32 = 450.0;
|
||||||
for &z in &[0.0_f32, span_mm] {
|
let support_zs: Vec<f32> = (0..=span_count).map(|i| span_mm * i as f32).collect();
|
||||||
|
for &z in &support_zs {
|
||||||
for i in 0..n_girders {
|
for i in 0..n_girders {
|
||||||
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
||||||
let bearing_ir = BearingIR {
|
let bearing_ir = BearingIR {
|
||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station: if z < 1.0 { 0.0 } else { span_m },
|
station: z as f64 / 1_000.0,
|
||||||
bearing_type: BearingType::Elastomeric,
|
bearing_type: BearingType::Elastomeric,
|
||||||
plan_length: BEARING_PLAN_LEN as f64,
|
plan_length: BEARING_PLAN_LEN as f64,
|
||||||
plan_width: BEARING_PLAN_WID as f64,
|
plan_width: BEARING_PLAN_WID as f64,
|
||||||
@@ -188,17 +246,30 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
};
|
};
|
||||||
let mut mesh = kernel.bearing_mesh(&bearing_ir)?;
|
let mut mesh = kernel.bearing_mesh(&bearing_ir)?;
|
||||||
mesh.recolor(COL_BEARING);
|
mesh.recolor(COL_BEARING);
|
||||||
// Z 중심 = 교대 위치(z). bearing mesh Z = [0, plan_length] → 오프셋 = z - plan_length/2.
|
|
||||||
parts.push(translate(mesh, x, 0.0, z - BEARING_PLAN_LEN * 0.5));
|
parts.push(translate(mesh, x, 0.0, z - BEARING_PLAN_LEN * 0.5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Abutments ──────────────────────────────────────────────────────────────
|
// ── Piers (내부 지점 span_count-1 개, Sprint 26) ──────────────────────────
|
||||||
|
for s in 1..span_count {
|
||||||
|
let pier_station_m = span_m * s as f64;
|
||||||
|
let pier_z = span_mm * s as f32;
|
||||||
|
let pier_ir = pier_ir_for_params(
|
||||||
|
pier_station_m, n_girders, spacing, girder_h, p.pier_type,
|
||||||
|
);
|
||||||
|
let mut mesh = kernel.pier_mesh(&pier_ir)?;
|
||||||
|
mesh.recolor(COL_PIER);
|
||||||
|
// pier_mesh 로컬 좌표: cap beam 상면이 Y=0 (거더 소핏 아래 bearing seat).
|
||||||
|
// Y 오프셋은 pier_mesh 자체가 정의하는 로컬 → 변환 불필요 (0).
|
||||||
|
parts.push(translate(mesh, 0.0, -BEARING_H, pier_z));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Abutments (양 끝) ─────────────────────────────────────────────────────
|
||||||
let wing = WingWallIR { length: 5_000.0, height: 2_500.0, thickness: 500.0 };
|
let wing = WingWallIR { length: 5_000.0, height: 2_500.0, thickness: 500.0 };
|
||||||
let total_w = (n_girders as f64 - 1.0) * spacing as f64 + 3_000.0;
|
let total_w = (n_girders as f64 - 1.0) * spacing as f64 + 3_000.0;
|
||||||
let breast_wall_h = (girder_h + BEARING_H) as f64;
|
let breast_wall_h = (girder_h + BEARING_H) as f64;
|
||||||
|
|
||||||
for &(station, z) in &[(0.0f64, -800.0_f32), (span_m, span_mm)] {
|
for &(station, z) in &[(0.0f64, -800.0_f32), (total_m, total_mm)] {
|
||||||
let abut_ir = AbutmentIR {
|
let abut_ir = AbutmentIR {
|
||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station,
|
station,
|
||||||
@@ -222,30 +293,30 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
|
|
||||||
// ── Ground plane ───────────────────────────────────────────────────────────
|
// ── Ground plane ───────────────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
let ground_y = -(BEARING_H + breast_wall_h as f32 + 1_000.0 + 200.0); // footing bottom - margin
|
let ground_y = -(BEARING_H + breast_wall_h as f32 + 1_000.0 + 200.0);
|
||||||
let hw = total_w as f32 * 0.5 + 8_000.0; // wider than bridge
|
let hw = total_w as f32 * 0.5 + 8_000.0;
|
||||||
let half_z = span_mm * 0.5 + 8_000.0;
|
let half_z = total_mm * 0.5 + 8_000.0;
|
||||||
let thickness = 500.0_f32;
|
let thickness = 500.0_f32;
|
||||||
let profile = vec![
|
let profile = vec![
|
||||||
[-hw, -thickness], [hw, -thickness], [hw, 0.0], [-hw, 0.0],
|
[-hw, -thickness], [hw, -thickness], [hw, 0.0], [-hw, 0.0],
|
||||||
];
|
];
|
||||||
let mut ground = cimery_kernel::sweep::sweep_profile_flat(&profile, half_z * 2.0);
|
let mut ground = cimery_kernel::sweep::sweep_profile_flat(&profile, half_z * 2.0);
|
||||||
ground.recolor(COL_GROUND);
|
ground.recolor(COL_GROUND);
|
||||||
parts.push(translate(ground, 0.0, ground_y, -half_z));
|
parts.push(translate(ground, 0.0, ground_y, total_mm * 0.5 - half_z));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Alignment centreline (optional) ───────────────────────────────────────
|
// ── Alignment centreline (optional) ───────────────────────────────────────
|
||||||
if p.show_alignment {
|
if p.show_alignment {
|
||||||
let radius = 80.0_f32;
|
let radius = 80.0_f32;
|
||||||
let mut align = cimery_kernel::sweep::polygon_prism(0.0, 0.0, radius, 8, span_mm);
|
let mut align = cimery_kernel::sweep::polygon_prism(0.0, 0.0, radius, 8, total_mm);
|
||||||
align.recolor(COL_ALIGNMENT);
|
align.recolor(COL_ALIGNMENT);
|
||||||
parts.push(translate(align, 0.0, girder_h * 0.5, 0.0));
|
parts.push(translate(align, 0.0, girder_h * 0.5, 0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Cross Beams (Sprint 19) ────────────────────────────────────────────────
|
// ── Cross Beams (경간마다) ────────────────────────────────────────────────
|
||||||
if p.show_cross_beams {
|
if p.show_cross_beams {
|
||||||
let interval_mm = (p.cross_beam_interval_m * 1_000.0) as f32;
|
let interval_mm = (p.cross_beam_interval_m * 1_000.0) as f32;
|
||||||
let num_beams = (span_mm / interval_mm).floor() as usize;
|
let num_per_span = (span_mm / interval_mm).floor() as usize;
|
||||||
let cb_ir_base = CrossBeamIR {
|
let cb_ir_base = CrossBeamIR {
|
||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station: 0.0,
|
station: 0.0,
|
||||||
@@ -258,26 +329,29 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
girder_spacing: spacing as f64,
|
girder_spacing: spacing as f64,
|
||||||
material: MaterialGrade::C50,
|
material: MaterialGrade::C50,
|
||||||
};
|
};
|
||||||
for i in 0..num_beams {
|
for s in 0..span_count {
|
||||||
let z = interval_mm * (i as f32 + 1.0);
|
let z_base = span_mm * s as f32;
|
||||||
let mut ir = cb_ir_base.clone();
|
for i in 0..num_per_span {
|
||||||
ir.id = FeatureId::new();
|
let z = z_base + interval_mm * (i as f32 + 1.0);
|
||||||
ir.station = z as f64 / 1_000.0;
|
let mut ir = cb_ir_base.clone();
|
||||||
if let Ok(mut mesh) = kernel.cross_beam_mesh(&ir) {
|
ir.id = FeatureId::new();
|
||||||
mesh.recolor(COL_CROSS_BEAM);
|
ir.station = z as f64 / 1_000.0;
|
||||||
parts.push(translate(mesh, 0.0, 0.0, z));
|
if let Ok(mut mesh) = kernel.cross_beam_mesh(&ir) {
|
||||||
|
mesh.recolor(COL_CROSS_BEAM);
|
||||||
|
parts.push(translate(mesh, 0.0, 0.0, z));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Expansion Joints (Sprint 19) ──────────────────────────────────────────
|
// ── Expansion Joints (양 교대 + 내부 피어 위치) ──────────────────────────
|
||||||
if p.show_expansion_joints {
|
if p.show_expansion_joints {
|
||||||
let deck_w = ((n_girders as f32 - 1.0) * spacing + 2_000.0) as f64;
|
let deck_w = ((n_girders as f32 - 1.0) * spacing + 2_000.0) as f64;
|
||||||
let y_top = girder_h + p.slab_thickness;
|
let y_top = girder_h + p.slab_thickness;
|
||||||
for &(station, z) in &[(0.0f64, 0.0_f32), (span_m, span_mm)] {
|
for &z in &support_zs {
|
||||||
let ej_ir = ExpansionJointIR {
|
let ej_ir = ExpansionJointIR {
|
||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station,
|
station: z as f64 / 1_000.0,
|
||||||
joint_type: ExpansionJointType::RubberType,
|
joint_type: ExpansionJointType::RubberType,
|
||||||
gap_width: 50.0,
|
gap_width: 50.0,
|
||||||
total_width: deck_w,
|
total_width: deck_w,
|
||||||
@@ -297,11 +371,14 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
// ─── Background scene (ground + alignment only, no features) ─────────────────
|
// ─── Background scene (ground + alignment only, no features) ─────────────────
|
||||||
|
|
||||||
/// Returns only the non-selectable background elements: ground plane + alignment line.
|
/// Returns only the non-selectable background elements: ground plane + alignment line.
|
||||||
|
/// Sprint 26: 다경간 지원 — ground·alignment 가 전체 교량 길이 기준.
|
||||||
pub fn build_background_scene(p: &SceneParams) -> Mesh {
|
pub fn build_background_scene(p: &SceneParams) -> Mesh {
|
||||||
let span_mm = (p.span_m * 1_000.0) as f32;
|
let span_mm = (p.span_m * 1_000.0) as f32;
|
||||||
let girder_h = p.girder_height;
|
let span_count = p.span_count.max(1).min(5);
|
||||||
let n_girders = p.girder_count.max(1).min(10);
|
let total_mm = span_mm * span_count as f32;
|
||||||
let spacing = p.girder_spacing;
|
let girder_h = p.girder_height;
|
||||||
|
let n_girders = p.girder_count.max(1).min(10);
|
||||||
|
let spacing = p.girder_spacing;
|
||||||
const BEARING_H: f32 = 60.0;
|
const BEARING_H: f32 = 60.0;
|
||||||
let breast_wall_h = (girder_h + BEARING_H) as f64;
|
let breast_wall_h = (girder_h + BEARING_H) as f64;
|
||||||
let total_w = (n_girders as f64 - 1.0) * spacing as f64 + 3_000.0;
|
let total_w = (n_girders as f64 - 1.0) * spacing as f64 + 3_000.0;
|
||||||
@@ -309,17 +386,17 @@ pub fn build_background_scene(p: &SceneParams) -> Mesh {
|
|||||||
|
|
||||||
let mut parts: Vec<Mesh> = Vec::new();
|
let mut parts: Vec<Mesh> = Vec::new();
|
||||||
|
|
||||||
// Ground
|
// Ground — 전체 교량 길이 + 양끝 8m 여유
|
||||||
let hw = total_w as f32 * 0.5 + 8_000.0;
|
let hw = total_w as f32 * 0.5 + 8_000.0;
|
||||||
let half_z = span_mm * 0.5 + 8_000.0;
|
let half_z = total_mm * 0.5 + 8_000.0;
|
||||||
let profile = vec![[-hw, -500.0_f32], [hw, -500.0], [hw, 0.0], [-hw, 0.0]];
|
let profile = vec![[-hw, -500.0_f32], [hw, -500.0], [hw, 0.0], [-hw, 0.0]];
|
||||||
let mut g = cimery_kernel::sweep::sweep_profile_flat(&profile, half_z * 2.0);
|
let mut g = cimery_kernel::sweep::sweep_profile_flat(&profile, half_z * 2.0);
|
||||||
g.recolor(COL_GROUND);
|
g.recolor(COL_GROUND);
|
||||||
parts.push(translate(g, 0.0, ground_y, -half_z));
|
parts.push(translate(g, 0.0, ground_y, total_mm * 0.5 - half_z));
|
||||||
|
|
||||||
// Alignment
|
// Alignment — 전체 교량 길이
|
||||||
if p.show_alignment {
|
if p.show_alignment {
|
||||||
let mut align = cimery_kernel::sweep::polygon_prism(0.0, 0.0, 80.0, 8, span_mm);
|
let mut align = cimery_kernel::sweep::polygon_prism(0.0, 0.0, 80.0, 8, total_mm);
|
||||||
align.recolor(COL_ALIGNMENT);
|
align.recolor(COL_ALIGNMENT);
|
||||||
parts.push(translate(align, 0.0, girder_h * 0.5, 0.0));
|
parts.push(translate(align, 0.0, girder_h * 0.5, 0.0));
|
||||||
}
|
}
|
||||||
@@ -346,37 +423,71 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
let girder_h = p.girder_height;
|
let girder_h = p.girder_height;
|
||||||
const BEARING_H: f32 = 60.0;
|
const BEARING_H: f32 = 60.0;
|
||||||
|
|
||||||
let section = PscISectionParams {
|
// Sprint 25: 단면 타입 하드코딩 제거. p.section_type 에 따라 PscI / SteelBox 분기.
|
||||||
total_height: girder_h as f64,
|
let (section_enum, section_type_enum) = match p.section_type {
|
||||||
top_flange_width: 600.0,
|
GirderSectionType::PscI => (
|
||||||
top_flange_thickness: 150.0,
|
SectionParams::PscI(PscISectionParams {
|
||||||
bottom_flange_width: 700.0,
|
total_height: girder_h as f64,
|
||||||
bottom_flange_thickness: 180.0,
|
top_flange_width: 600.0,
|
||||||
web_thickness: 200.0,
|
top_flange_thickness: 150.0,
|
||||||
haunch: 50.0,
|
bottom_flange_width: 700.0,
|
||||||
|
bottom_flange_thickness: 180.0,
|
||||||
|
web_thickness: 200.0,
|
||||||
|
haunch: 50.0,
|
||||||
|
}),
|
||||||
|
SectionType::PscI,
|
||||||
|
),
|
||||||
|
GirderSectionType::SteelBox => {
|
||||||
|
use cimery_ir::SteelBoxParams;
|
||||||
|
let h = girder_h as f64;
|
||||||
|
(
|
||||||
|
SectionParams::SteelBox(SteelBoxParams {
|
||||||
|
total_height: h,
|
||||||
|
top_width: h * 1.2,
|
||||||
|
bottom_width: h * 0.8,
|
||||||
|
web_thickness: 20.0,
|
||||||
|
top_flange_thickness: 25.0,
|
||||||
|
bottom_flange_thickness: 22.0,
|
||||||
|
}),
|
||||||
|
SectionType::SteelBox,
|
||||||
|
)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let span_count = p.span_count.max(1).min(5);
|
||||||
|
let total_m = span_m * span_count as f64;
|
||||||
|
let total_mm = span_mm * span_count as f32;
|
||||||
|
|
||||||
let mut out: Vec<FeatureMesh> = Vec::new();
|
let mut out: Vec<FeatureMesh> = Vec::new();
|
||||||
|
|
||||||
// Girders
|
// Girders (경간마다 독립 세트)
|
||||||
for i in 0..n_girders {
|
for s in 0..span_count {
|
||||||
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
let z_base = span_mm * s as f32;
|
||||||
let ir = GirderIR {
|
let s_start = span_m * s as f64;
|
||||||
id: FeatureId::new(), station_start: 0.0, station_end: span_m,
|
for i in 0..n_girders {
|
||||||
offset_from_alignment: x as f64, section_type: SectionType::PscI,
|
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
||||||
section: SectionParams::PscI(section.clone()),
|
let ir = GirderIR {
|
||||||
count: 1, spacing: 0.0, material: MaterialGrade::C50,
|
id: FeatureId::new(), station_start: s_start, station_end: s_start + span_m,
|
||||||
};
|
offset_from_alignment: x as f64, section_type: section_type_enum,
|
||||||
let mut mesh = kernel.girder_mesh(&ir)?;
|
section: section_enum.clone(),
|
||||||
mesh.recolor(COL_GIRDER);
|
count: 1, spacing: 0.0, material: MaterialGrade::C50,
|
||||||
for v in &mut mesh.vertices { v[0] += x; }
|
};
|
||||||
out.push(FeatureMesh { mesh, label: format!("거더 {}", i + 1) });
|
let mut mesh = kernel.girder_mesh(&ir)?;
|
||||||
|
mesh.recolor(COL_GIRDER);
|
||||||
|
for v in &mut mesh.vertices { v[0] += x; v[2] += z_base; }
|
||||||
|
let label = if span_count > 1 {
|
||||||
|
format!("거더 {}-{}", s + 1, i + 1)
|
||||||
|
} else {
|
||||||
|
format!("거더 {}", i + 1)
|
||||||
|
};
|
||||||
|
out.push(FeatureMesh { mesh, label });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deck Slab (one unit)
|
// Deck Slab (전 구간 연속)
|
||||||
let half_w = ((n_girders as f32 - 1.0) * spacing) * 0.5 + 1_000.0;
|
let half_w = ((n_girders as f32 - 1.0) * spacing) * 0.5 + 1_000.0;
|
||||||
let deck_ir = DeckSlabIR {
|
let deck_ir = DeckSlabIR {
|
||||||
id: FeatureId::new(), station_start: 0.0, station_end: span_m,
|
id: FeatureId::new(), station_start: 0.0, station_end: total_m,
|
||||||
width_left: half_w as f64, width_right: half_w as f64,
|
width_left: half_w as f64, width_right: half_w as f64,
|
||||||
thickness: p.slab_thickness as f64, haunch_depth: 0.0,
|
thickness: p.slab_thickness as f64, haunch_depth: 0.0,
|
||||||
cross_slope: 2.0, material: MaterialGrade::C40,
|
cross_slope: 2.0, material: MaterialGrade::C40,
|
||||||
@@ -386,14 +497,14 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
for v in &mut deck.vertices { v[1] += girder_h + p.slab_thickness; }
|
for v in &mut deck.vertices { v[1] += girder_h + p.slab_thickness; }
|
||||||
out.push(FeatureMesh { mesh: deck, label: "바닥판 슬래브".into() });
|
out.push(FeatureMesh { mesh: deck, label: "바닥판 슬래브".into() });
|
||||||
|
|
||||||
// Bearings
|
// Bearings (모든 지점)
|
||||||
// plan_length(350mm) = 경간 방향 → Z 센터링 오프셋 = z - 175.
|
|
||||||
const SEL_BEARING_LEN: f32 = 350.0;
|
const SEL_BEARING_LEN: f32 = 350.0;
|
||||||
for &z in &[0.0_f32, span_mm] {
|
let support_zs: Vec<f32> = (0..=span_count).map(|i| span_mm * i as f32).collect();
|
||||||
|
for (sup_idx, &z) in support_zs.iter().enumerate() {
|
||||||
for i in 0..n_girders {
|
for i in 0..n_girders {
|
||||||
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
let x = (i as f32 - (n_girders as f32 - 1.0) * 0.5) * spacing;
|
||||||
let bir = BearingIR {
|
let bir = BearingIR {
|
||||||
id: FeatureId::new(), station: if z < 1.0 { 0.0 } else { span_m },
|
id: FeatureId::new(), station: z as f64 / 1_000.0,
|
||||||
bearing_type: BearingType::Elastomeric,
|
bearing_type: BearingType::Elastomeric,
|
||||||
plan_length: SEL_BEARING_LEN as f64, plan_width: 450.0,
|
plan_length: SEL_BEARING_LEN as f64, plan_width: 450.0,
|
||||||
total_height: BEARING_H as f64, capacity_vertical: 1_500.0,
|
total_height: BEARING_H as f64, capacity_vertical: 1_500.0,
|
||||||
@@ -401,16 +512,31 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
let mut mesh = kernel.bearing_mesh(&bir)?;
|
let mut mesh = kernel.bearing_mesh(&bir)?;
|
||||||
mesh.recolor(COL_BEARING);
|
mesh.recolor(COL_BEARING);
|
||||||
for v in &mut mesh.vertices { v[0] += x; v[2] += z - SEL_BEARING_LEN * 0.5; }
|
for v in &mut mesh.vertices { v[0] += x; v[2] += z - SEL_BEARING_LEN * 0.5; }
|
||||||
let side = if z < 1.0 { "시작" } else { "종점" };
|
let side = if sup_idx == 0 { "시작".to_string() }
|
||||||
|
else if sup_idx == span_count { "종점".to_string() }
|
||||||
|
else { format!("P{}", sup_idx) };
|
||||||
out.push(FeatureMesh { mesh, label: format!("받침 {}-{}", side, i + 1) });
|
out.push(FeatureMesh { mesh, label: format!("받침 {}-{}", side, i + 1) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abutments
|
// Piers (내부 지점, Sprint 26)
|
||||||
|
for s in 1..span_count {
|
||||||
|
let pier_station_m = span_m * s as f64;
|
||||||
|
let pier_z = span_mm * s as f32;
|
||||||
|
let pier_ir = pier_ir_for_params(
|
||||||
|
pier_station_m, n_girders, spacing, girder_h, p.pier_type,
|
||||||
|
);
|
||||||
|
let mut mesh = kernel.pier_mesh(&pier_ir)?;
|
||||||
|
mesh.recolor(COL_PIER);
|
||||||
|
for v in &mut mesh.vertices { v[1] -= BEARING_H; v[2] += pier_z; }
|
||||||
|
out.push(FeatureMesh { mesh, label: format!("교각 P{}", s) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abutments (양 끝)
|
||||||
let total_w = (n_girders as f64 - 1.0) * spacing as f64 + 3_000.0;
|
let total_w = (n_girders as f64 - 1.0) * spacing as f64 + 3_000.0;
|
||||||
let bwh = (girder_h + BEARING_H) as f64;
|
let bwh = (girder_h + BEARING_H) as f64;
|
||||||
let wing = WingWallIR { length: 5_000.0, height: 2_500.0, thickness: 500.0 };
|
let wing = WingWallIR { length: 5_000.0, height: 2_500.0, thickness: 500.0 };
|
||||||
for &(station, z) in &[(0.0f64, -800.0_f32), (span_m, span_mm)] {
|
for &(station, z) in &[(0.0f64, -800.0_f32), (total_m, total_mm)] {
|
||||||
let air = AbutmentIR {
|
let air = AbutmentIR {
|
||||||
id: FeatureId::new(), station, skew_angle: 0.0,
|
id: FeatureId::new(), station, skew_angle: 0.0,
|
||||||
abutment_type: AbutmentType::ReverseT,
|
abutment_type: AbutmentType::ReverseT,
|
||||||
@@ -428,10 +554,10 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
out.push(FeatureMesh { mesh, label: format!("교대 ({})", side) });
|
out.push(FeatureMesh { mesh, label: format!("교대 ({})", side) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Cross Beams (Sprint 19) ────────────────────────────────────────────
|
// Cross Beams (경간마다)
|
||||||
if p.show_cross_beams {
|
if p.show_cross_beams {
|
||||||
let interval_mm = (p.cross_beam_interval_m * 1_000.0) as f32;
|
let interval_mm = (p.cross_beam_interval_m * 1_000.0) as f32;
|
||||||
let num_beams = (span_mm / interval_mm).floor() as usize;
|
let num_per_span = (span_mm / interval_mm).floor() as usize;
|
||||||
let cb_ir_base = CrossBeamIR {
|
let cb_ir_base = CrossBeamIR {
|
||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station: 0.0,
|
station: 0.0,
|
||||||
@@ -444,29 +570,28 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
girder_spacing: spacing as f64,
|
girder_spacing: spacing as f64,
|
||||||
material: MaterialGrade::C50,
|
material: MaterialGrade::C50,
|
||||||
};
|
};
|
||||||
for i in 0..num_beams {
|
for s in 0..span_count {
|
||||||
let z = interval_mm * (i as f32 + 1.0);
|
let z_base = span_mm * s as f32;
|
||||||
let mut ir = cb_ir_base.clone();
|
for i in 0..num_per_span {
|
||||||
ir.id = FeatureId::new();
|
let z = z_base + interval_mm * (i as f32 + 1.0);
|
||||||
ir.station = z as f64 / 1_000.0;
|
let mut ir = cb_ir_base.clone();
|
||||||
let mut mesh = kernel.cross_beam_mesh(&ir)?;
|
ir.id = FeatureId::new();
|
||||||
mesh.recolor(COL_CROSS_BEAM);
|
ir.station = z as f64 / 1_000.0;
|
||||||
let half = ir.total_length_mm() as f32 * 0.5;
|
let mut mesh = kernel.cross_beam_mesh(&ir)?;
|
||||||
for v in &mut mesh.vertices {
|
mesh.recolor(COL_CROSS_BEAM);
|
||||||
v[2] += z;
|
for v in &mut mesh.vertices { v[2] += z; }
|
||||||
|
out.push(FeatureMesh { mesh, label: format!("가로보 @ {:.0}m", z / 1_000.0) });
|
||||||
}
|
}
|
||||||
let _ = half; // translation already applied in cross_beam builder
|
|
||||||
out.push(FeatureMesh { mesh, label: format!("가로보 @ {:.0}m", z / 1_000.0) });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Expansion Joints (Sprint 19) ───────────────────────────────────────
|
// ── Expansion Joints (모든 지점) ───────────────────────────────────────
|
||||||
if p.show_expansion_joints {
|
if p.show_expansion_joints {
|
||||||
let deck_w = ((n_girders as f32 - 1.0) * spacing + 2_000.0) as f64;
|
let deck_w = ((n_girders as f32 - 1.0) * spacing + 2_000.0) as f64;
|
||||||
for &(station, z) in &[(0.0f64, 0.0_f32), (span_m, span_mm)] {
|
for (sup_idx, &z) in support_zs.iter().enumerate() {
|
||||||
let ej_ir = ExpansionJointIR {
|
let ej_ir = ExpansionJointIR {
|
||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station,
|
station: z as f64 / 1_000.0,
|
||||||
joint_type: ExpansionJointType::RubberType,
|
joint_type: ExpansionJointType::RubberType,
|
||||||
gap_width: 50.0,
|
gap_width: 50.0,
|
||||||
total_width: deck_w,
|
total_width: deck_w,
|
||||||
@@ -480,7 +605,9 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
v[1] += y_top;
|
v[1] += y_top;
|
||||||
v[2] += z;
|
v[2] += z;
|
||||||
}
|
}
|
||||||
let side = if z < 1.0 { "시작" } else { "종점" };
|
let side = if sup_idx == 0 { "시작".to_string() }
|
||||||
|
else if sup_idx == span_count { "종점".to_string() }
|
||||||
|
else { format!("P{}", sup_idx) };
|
||||||
out.push(FeatureMesh { mesh, label: format!("신축이음 ({})", side) });
|
out.push(FeatureMesh { mesh, label: format!("신축이음 ({})", side) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -489,10 +616,11 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bounding box of the full bridge scene (for camera setup).
|
/// Bounding box of the full bridge scene (for camera setup).
|
||||||
|
/// Sprint 26: 다경간 지원 → Z 범위가 span_m × span_count.
|
||||||
pub fn scene_extents(p: &SceneParams) -> ([f32; 3], [f32; 3]) {
|
pub fn scene_extents(p: &SceneParams) -> ([f32; 3], [f32; 3]) {
|
||||||
let span_mm = (p.span_m * 1_000.0) as f32;
|
let total_mm = (p.span_m * 1_000.0) as f32 * p.span_count.max(1) as f32;
|
||||||
let half_w = ((p.girder_count as f32 - 1.0) * p.girder_spacing * 0.5 + 2_000.0).max(5_000.0);
|
let half_w = ((p.girder_count as f32 - 1.0) * p.girder_spacing * 0.5 + 2_000.0).max(5_000.0);
|
||||||
let top_y = p.girder_height + p.slab_thickness + 200.0;
|
let top_y = p.girder_height + p.slab_thickness + 200.0;
|
||||||
let bot_y = -(p.girder_height + 3_000.0 + 1_000.0);
|
let bot_y = -(p.girder_height + 3_000.0 + 1_000.0);
|
||||||
([-half_w, bot_y, -2_000.0], [half_w, top_y, span_mm + 2_000.0])
|
([-half_w, bot_y, -2_000.0], [half_w, top_y, total_mm + 2_000.0])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -624,6 +624,15 @@ impl RenderState {
|
|||||||
ps!("c/c 간격 (mm)", &mut p.girder_spacing, 1_500.0..=4_000.0, 100.0);
|
ps!("c/c 간격 (mm)", &mut p.girder_spacing, 1_500.0..=4_000.0, 100.0);
|
||||||
ps!("거더 높이 (mm)", &mut p.girder_height, 1_000.0..=3_000.0, 100.0);
|
ps!("거더 높이 (mm)", &mut p.girder_height, 1_000.0..=3_000.0, 100.0);
|
||||||
ps!("슬래브 두께 (mm)",&mut p.slab_thickness, 150.0..=400.0, 10.0);
|
ps!("슬래브 두께 (mm)",&mut p.slab_thickness, 150.0..=400.0, 10.0);
|
||||||
|
// Sprint 26: 다경간 지원
|
||||||
|
ps!("경간 수", &mut p.span_count, 1..=5, 1.0);
|
||||||
|
ui.label("교각 형식");
|
||||||
|
let prev_pt = p.pier_type;
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.selectable_value(&mut p.pier_type, cimery_core::PierType::SingleColumn, "T형(단주)");
|
||||||
|
ui.selectable_value(&mut p.pier_type, cimery_core::PierType::MultiColumn, "π형(다주)");
|
||||||
|
});
|
||||||
|
if p.pier_type != prev_pt { dirty = true; }
|
||||||
|
|
||||||
ui.label("단면 형식");
|
ui.label("단면 형식");
|
||||||
let prev_sec = p.section_type;
|
let prev_sec = p.section_type;
|
||||||
|
|||||||
@@ -30,15 +30,22 @@ pub struct ProjectFile {
|
|||||||
pub cross_beam_interval_m: f64,
|
pub cross_beam_interval_m: f64,
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub show_expansion_joints: bool,
|
pub show_expansion_joints: bool,
|
||||||
|
/// Sprint 26: 다경간
|
||||||
|
#[serde(default = "default_span_count")]
|
||||||
|
pub span_count: usize,
|
||||||
|
#[serde(default = "default_pier_type")]
|
||||||
|
pub pier_type: String, // "single" | "multi"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_true() -> bool { true }
|
fn default_true() -> bool { true }
|
||||||
fn default_cross_beam_interval() -> f64 { 5.0 }
|
fn default_cross_beam_interval() -> f64 { 5.0 }
|
||||||
|
fn default_span_count() -> usize { 1 }
|
||||||
|
fn default_pier_type() -> String { "single".into() }
|
||||||
|
|
||||||
impl ProjectFile {
|
impl ProjectFile {
|
||||||
pub fn from_params(name: &str, p: &SceneParams) -> Self {
|
pub fn from_params(name: &str, p: &SceneParams) -> Self {
|
||||||
Self {
|
Self {
|
||||||
version: 1,
|
version: 2,
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
span_m: p.span_m,
|
span_m: p.span_m,
|
||||||
girder_count: p.girder_count,
|
girder_count: p.girder_count,
|
||||||
@@ -53,6 +60,11 @@ impl ProjectFile {
|
|||||||
show_cross_beams: p.show_cross_beams,
|
show_cross_beams: p.show_cross_beams,
|
||||||
cross_beam_interval_m: p.cross_beam_interval_m,
|
cross_beam_interval_m: p.cross_beam_interval_m,
|
||||||
show_expansion_joints: p.show_expansion_joints,
|
show_expansion_joints: p.show_expansion_joints,
|
||||||
|
span_count: p.span_count,
|
||||||
|
pier_type: match p.pier_type {
|
||||||
|
cimery_core::PierType::MultiColumn => "multi".into(),
|
||||||
|
_ => "single".into(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +83,11 @@ impl ProjectFile {
|
|||||||
show_cross_beams: self.show_cross_beams,
|
show_cross_beams: self.show_cross_beams,
|
||||||
cross_beam_interval_m: self.cross_beam_interval_m,
|
cross_beam_interval_m: self.cross_beam_interval_m,
|
||||||
show_expansion_joints: self.show_expansion_joints,
|
show_expansion_joints: self.show_expansion_joints,
|
||||||
|
span_count: self.span_count,
|
||||||
|
pier_type: match self.pier_type.as_str() {
|
||||||
|
"multi" => cimery_core::PierType::MultiColumn,
|
||||||
|
_ => cimery_core::PierType::SingleColumn,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user