"""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)