fix: 거더 높이 변경 시 씬 미갱신 + 받침 치수 오류 수정
[버그 1] Apply 후 카메라 자동 피트 누락
- rebuild_mesh() 에서 scene_extents 만 업데이트하고 camera target/radius 는
갱신하지 않아, 거더 높이를 올려도 카메라가 구 씬 중심을 가리켜 변화가 안 보임.
- 수정: rebuild_mesh() 끝에 camera.zoom_extents(mn, mx) + update_camera() 추가.
[버그 2] bearing.rs plan_length / plan_width 방향 오류
- plan_length(350mm, 경간 방향)를 profile X(횡방향)에, plan_width(450mm, 횡방향)를
sweep Z(경간방향)에 사용 → 받침이 90° 회전된 치수로 생성됨.
- Z 센터링 오프셋도 plan_width/2=225mm 로 계산 → 올바른 plan_length/2=175mm 보다
50mm 더 교대 밖으로 튀어나옴 (스크린샷의 부유 블록).
- 수정:
· bearing.rs: trans_dim=plan_width(profile X), span_dim=plan_length(sweep Z)
· bridge_scene.rs: build_bridge_scene + build_selectable_scene 의 Z 오프셋을
z - 225 → z - plan_length/2 = z - 175 로 변경.
cargo check --workspace 0 errors/warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,18 +7,20 @@ pub fn build_bearing_mesh(ir: &BearingIR) -> Result<Mesh, KernelError> {
|
|||||||
if ir.plan_length <= 0.0 || ir.plan_width <= 0.0 || ir.total_height <= 0.0 {
|
if ir.plan_length <= 0.0 || ir.plan_width <= 0.0 || ir.total_height <= 0.0 {
|
||||||
return Err(KernelError::InvalidInput("bearing dimensions must be positive".into()));
|
return Err(KernelError::InvalidInput("bearing dimensions must be positive".into()));
|
||||||
}
|
}
|
||||||
// Box: X=[-l/2, +l/2] centred, Y=[-h, 0] (top at Y=0 = girder soffit), Z=[0, w]
|
// 좌표계: X = 횡방향(transverse), Y = 높이(vertical, top = 0), Z = 경간방향(span).
|
||||||
// Caller translates to girder X offset; Y=0 means bearing top sits at soffit.
|
// plan_length = 경간 방향 치수 [mm] → sweep 축(Z).
|
||||||
let l = ir.plan_length as f32;
|
// plan_width = 횡방향 치수 [mm] → profile X 축.
|
||||||
|
// Y=0 = 거더 소핏(soffit); bearing 상부가 소핏에 밀착.
|
||||||
|
let trans_dim = ir.plan_width as f32; // 횡 방향 (profile X)
|
||||||
|
let span_dim = ir.plan_length as f32; // 경간 방향 (sweep Z)
|
||||||
let h = ir.total_height as f32;
|
let h = ir.total_height as f32;
|
||||||
let w = ir.plan_width as f32;
|
|
||||||
let profile = vec![
|
let profile = vec![
|
||||||
[-l * 0.5, -h ],
|
[-trans_dim * 0.5, -h ],
|
||||||
[ l * 0.5, -h ],
|
[ trans_dim * 0.5, -h ],
|
||||||
[ l * 0.5, 0.0],
|
[ trans_dim * 0.5, 0.0],
|
||||||
[-l * 0.5, 0.0],
|
[-trans_dim * 0.5, 0.0],
|
||||||
];
|
];
|
||||||
Ok(sweep::sweep_profile_flat(&profile, w))
|
Ok(sweep::sweep_profile_flat(&profile, span_dim))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
|
|
||||||
// ── Bearings ───────────────────────────────────────────────────────────────
|
// ── Bearings ───────────────────────────────────────────────────────────────
|
||||||
// 5 per abutment, one under each girder
|
// 5 per abutment, one under each girder
|
||||||
|
// plan_length(350mm) = 경간 방향 → half = 175mm 으로 Z 센터링.
|
||||||
|
const BEARING_PLAN_LEN: f32 = 350.0;
|
||||||
|
const BEARING_PLAN_WID: f32 = 450.0;
|
||||||
for &z in &[0.0_f32, span_mm] {
|
for &z in &[0.0_f32, span_mm] {
|
||||||
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;
|
||||||
@@ -178,14 +181,15 @@ pub fn build_bridge_scene<K: GeomKernel>(kernel: &K, p: &SceneParams) -> Result<
|
|||||||
id: FeatureId::new(),
|
id: FeatureId::new(),
|
||||||
station: if z < 1.0 { 0.0 } else { span_m },
|
station: if z < 1.0 { 0.0 } else { span_m },
|
||||||
bearing_type: BearingType::Elastomeric,
|
bearing_type: BearingType::Elastomeric,
|
||||||
plan_length: 350.0,
|
plan_length: BEARING_PLAN_LEN as f64,
|
||||||
plan_width: 450.0,
|
plan_width: BEARING_PLAN_WID as f64,
|
||||||
total_height: BEARING_H as f64,
|
total_height: BEARING_H as f64,
|
||||||
capacity_vertical: 1_500.0,
|
capacity_vertical: 1_500.0,
|
||||||
};
|
};
|
||||||
let mut mesh = kernel.bearing_mesh(&bearing_ir)?;
|
let mut mesh = kernel.bearing_mesh(&bearing_ir)?;
|
||||||
mesh.recolor(COL_BEARING);
|
mesh.recolor(COL_BEARING);
|
||||||
parts.push(translate(mesh, x, 0.0, z - 225.0));
|
// 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,18 +387,20 @@ pub fn build_selectable_scene<K: GeomKernel>(
|
|||||||
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;
|
||||||
for &z in &[0.0_f32, span_mm] {
|
for &z in &[0.0_f32, span_mm] {
|
||||||
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: if z < 1.0 { 0.0 } else { span_m },
|
||||||
bearing_type: BearingType::Elastomeric,
|
bearing_type: BearingType::Elastomeric,
|
||||||
plan_length: 350.0, 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,
|
||||||
};
|
};
|
||||||
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 - 225.0; }
|
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 z < 1.0 { "시작" } else { "종점" };
|
||||||
out.push(FeatureMesh { mesh, label: format!("받침 {}-{}", side, i + 1) });
|
out.push(FeatureMesh { mesh, label: format!("받침 {}-{}", side, i + 1) });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,6 +492,9 @@ impl RenderState {
|
|||||||
let (mn, mx) = scene_extents(&self.params);
|
let (mn, mx) = scene_extents(&self.params);
|
||||||
self.scene_mn = mn;
|
self.scene_mn = mn;
|
||||||
self.scene_mx = mx;
|
self.scene_mx = mx;
|
||||||
|
// Apply 후 씬 범위에 맞게 카메라 자동 피트 (거더 높이 변경 등이 즉시 보이도록).
|
||||||
|
self.camera.zoom_extents(mn, mx);
|
||||||
|
self.update_camera();
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user