- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
155 lines
4.2 KiB
Python
155 lines
4.2 KiB
Python
"""rebar_layout 테스트 (Rhino 없이 실행됩니다).
|
|
|
|
기준: docs/design_rules.md, 기존 프로젝트 rebar_pipeline/README.md §3
|
|
"""
|
|
import pytest
|
|
|
|
import rebar_layout as rl
|
|
|
|
|
|
# ---- 중심에서 바깥으로 배치 ----
|
|
|
|
def test_center_out_even_count_has_no_bar_on_center():
|
|
assert rl.center_out_positions(4, 250) == [-375, -125, 125, 375]
|
|
|
|
|
|
def test_center_out_odd_count_has_bar_on_center():
|
|
assert rl.center_out_positions(3, 250) == [-250, 0, 250]
|
|
|
|
|
|
def test_center_out_13_bars_goes_to_1500():
|
|
# 2CYCLE: 0, ±250 ... ±1500
|
|
pos = rl.center_out_positions(13, 250)
|
|
assert len(pos) == 13
|
|
assert pos[0] == -1500
|
|
assert pos[-1] == 1500
|
|
assert 0 in pos
|
|
|
|
|
|
def test_center_out_zero_bars_is_empty():
|
|
assert rl.center_out_positions(0, 250) == []
|
|
|
|
|
|
def test_center_out_rejects_bad_input():
|
|
with pytest.raises(ValueError):
|
|
rl.center_out_positions(-1, 250)
|
|
with pytest.raises(ValueError):
|
|
rl.center_out_positions(5, 0)
|
|
|
|
|
|
# ---- 바깥 철근을 피복에 맞추는 배치 (1CYCLE) ----
|
|
|
|
def test_snapped_array_matches_1cycle_drawing():
|
|
# 두부보 폭 3500, 피복 120, 14본, 간격 250
|
|
pos = rl.snapped_array(14, 250, 3500, 120)
|
|
assert len(pos) == 14
|
|
assert pos[0] == -1630
|
|
assert pos[1] == -1375
|
|
assert pos[6] == -125
|
|
assert pos[7] == 125
|
|
assert pos[-2] == 1375
|
|
assert pos[-1] == 1630
|
|
|
|
|
|
def test_snapped_array_is_symmetric_and_sorted():
|
|
pos = rl.snapped_array(14, 250, 3500, 120)
|
|
assert pos == sorted(pos)
|
|
assert pos == [-p for p in reversed(pos)]
|
|
|
|
|
|
def test_snapped_array_outer_bars_sit_at_cover():
|
|
pos = rl.snapped_array(14, 250, 3500, 120)
|
|
assert max(pos) == 3500 / 2 - 120
|
|
|
|
|
|
def test_snapped_array_rejects_too_few_bars():
|
|
with pytest.raises(ValueError):
|
|
rl.snapped_array(1, 250, 3500, 120)
|
|
|
|
|
|
def test_snapped_array_rejects_when_inner_bars_pass_outer_bars():
|
|
# 폭이 너무 좁아서 안쪽 철근이 바깥 철근을 넘어감
|
|
with pytest.raises(ValueError):
|
|
rl.snapped_array(14, 250, 2000, 120)
|
|
|
|
|
|
# ---- 수평 순간격 (KDS 24 14 21 4.5.3) ----
|
|
|
|
def test_min_horizontal_clear_h25_is_40mm():
|
|
# max(1.5*25.4=38.1, 1.5*25=37.5, 40) = 40
|
|
assert rl.min_horizontal_clear("H25", gmax=25) == pytest.approx(40.0)
|
|
|
|
|
|
def test_min_horizontal_clear_h29_uses_1_5db():
|
|
assert rl.min_horizontal_clear("H29", gmax=25) == pytest.approx(1.5 * 28.6)
|
|
|
|
|
|
def test_min_horizontal_clear_large_aggregate_governs():
|
|
assert rl.min_horizontal_clear("H25", gmax=40) == pytest.approx(60.0)
|
|
|
|
|
|
def test_clear_spacing_is_center_gap_minus_diameter():
|
|
assert rl.clear_spacing(250, "H25") == pytest.approx(250 - 25.4)
|
|
|
|
|
|
def test_horizontal_spacing_ok_for_250_array():
|
|
pos = rl.snapped_array(14, 250, 3500, 120)
|
|
assert rl.horizontal_spacing_ok(pos, "H25", gmax=25)
|
|
|
|
|
|
def test_horizontal_spacing_fails_when_too_close():
|
|
# 중심 간격 50 -> 순간격 24.6mm < 40mm
|
|
assert not rl.horizontal_spacing_ok([0, 50], "H25", gmax=25)
|
|
|
|
|
|
# ---- 다단 연직 배치 ----
|
|
|
|
def test_min_vertical_clear_h25_is_db():
|
|
# max(25, 25.4) = 25.4
|
|
assert rl.min_vertical_clear("H25") == pytest.approx(25.4)
|
|
|
|
|
|
def test_min_vertical_clear_small_bar_uses_25mm():
|
|
assert rl.min_vertical_clear("H16") == pytest.approx(25.0)
|
|
|
|
|
|
def test_vertical_pitch_100_ok_for_h25():
|
|
assert rl.vertical_pitch_ok("H25", 100)
|
|
|
|
|
|
def test_vertical_pitch_40_fails_for_h25():
|
|
# 순간격 14.6mm < 25.4mm
|
|
assert not rl.vertical_pitch_ok("H25", 40)
|
|
|
|
|
|
def test_tier_elevations_go_down_from_top_cover():
|
|
assert rl.tier_elevations(1000, 120, 2, 100) == [880, 780]
|
|
|
|
|
|
def test_tier_elevations_single_tier():
|
|
assert rl.tier_elevations(1000, 120, 1, 100) == [880]
|
|
|
|
|
|
# ---- 수량, 중량 ----
|
|
|
|
def test_unit_weight_h25():
|
|
assert rl.unit_weight("H25") == pytest.approx(3.98)
|
|
|
|
|
|
def test_unit_weight_unknown_bar_raises():
|
|
with pytest.raises(ValueError):
|
|
rl.unit_weight("H99")
|
|
|
|
|
|
def test_bar_weight_one_bar():
|
|
# H25, 9.930m 1본 = 3.98 * 9.93
|
|
assert rl.bar_weight("H25", 9930) == pytest.approx(3.98 * 9.93)
|
|
|
|
|
|
def test_array_summary_2cycle_main_bars():
|
|
pos = rl.center_out_positions(13, 250)
|
|
s = rl.array_summary("H25", 9930, pos)
|
|
assert s["count"] == 13
|
|
assert s["total_length_m"] == pytest.approx(13 * 9.93)
|
|
assert s["weight_kg"] == pytest.approx(3.98 * 9.93 * 13)
|