chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- 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>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,168 @@
|
||||
"""frame 테스트 (Rhino 없이 실행됩니다).
|
||||
|
||||
월드 좌표 (x, y, z) <-> 코핑 로컬 좌표 (s, t, z)
|
||||
s = 길이방향 (코핑 축), t = 폭방향 (s를 반시계로 90도 돌린 방향), z = 월드 높이 그대로
|
||||
기존 프로젝트: rebar_pipeline/rhino/07_ctc_extract.py 의 OX, OY, ANG 방식과 같습니다.
|
||||
"""
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
import frame as fr
|
||||
|
||||
|
||||
# ---- 기본 변환 ----
|
||||
|
||||
def test_identity_frame_keeps_coordinates():
|
||||
f = fr.Frame(0.0, 0.0, 0.0)
|
||||
assert f.to_local(10.0, 20.0, 5.0) == pytest.approx((10.0, 20.0, 5.0))
|
||||
|
||||
|
||||
def test_origin_maps_to_zero_zero():
|
||||
f = fr.Frame(1000.0, 2000.0, 0.3)
|
||||
s, t, z = f.to_local(1000.0, 2000.0, 77.0)
|
||||
assert (s, t) == pytest.approx((0.0, 0.0))
|
||||
assert z == 77.0
|
||||
|
||||
|
||||
def test_z_is_not_changed():
|
||||
f = fr.Frame(1000.0, 2000.0, 0.3)
|
||||
assert f.to_local(1500.0, 2500.0, 9331.0)[2] == 9331.0
|
||||
assert f.to_world(10.0, 20.0, 9331.0)[2] == 9331.0
|
||||
|
||||
|
||||
def test_angle_90_degrees_swaps_axes():
|
||||
f = fr.Frame(0.0, 0.0, math.pi / 2)
|
||||
# 월드 +Y 방향이 로컬 +s 방향
|
||||
assert f.to_local(0.0, 100.0, 0.0) == pytest.approx((100.0, 0.0, 0.0))
|
||||
# 월드 -X 방향이 로컬 +t 방향
|
||||
assert f.to_local(-50.0, 0.0, 0.0) == pytest.approx((0.0, 50.0, 0.0))
|
||||
|
||||
|
||||
def test_angle_45_degrees():
|
||||
f = fr.Frame(0.0, 0.0, math.pi / 4)
|
||||
r = math.sqrt(2.0) * 50.0
|
||||
assert f.to_local(50.0, 50.0, 0.0) == pytest.approx((r, 0.0, 0.0))
|
||||
|
||||
|
||||
def test_to_world_basic():
|
||||
f = fr.Frame(100.0, 200.0, math.pi / 2)
|
||||
# 로컬 s=10 -> 월드 +Y 방향으로 10
|
||||
assert f.to_world(10.0, 0.0, 0.0) == pytest.approx((100.0, 210.0, 0.0))
|
||||
# 로컬 t=10 -> 월드 -X 방향으로 10
|
||||
assert f.to_world(0.0, 10.0, 0.0) == pytest.approx((90.0, 200.0, 0.0))
|
||||
|
||||
|
||||
# ---- 왕복 변환 (월드 -> 로컬 -> 월드가 원래 값이어야 함) ----
|
||||
|
||||
@pytest.mark.parametrize("point", [
|
||||
(480708784.0, 153831202.0, 7738.0),
|
||||
(480721068.0, 153835457.0, -342.0),
|
||||
(0.0, 0.0, 0.0),
|
||||
(-5.5, 1234.5, 10.0),
|
||||
])
|
||||
def test_round_trip_world_local_world(point):
|
||||
f = fr.from_two_points((480708784.0, 153831202.0), (480721068.0, 153835457.0))
|
||||
back = f.to_world(*f.to_local(*point))
|
||||
assert back == pytest.approx(point, abs=1e-6)
|
||||
|
||||
|
||||
def test_round_trip_local_world_local():
|
||||
f = fr.Frame(480714926.0, 153833329.5, 0.3333)
|
||||
local = (1234.5, -678.9, 10405.3)
|
||||
back = f.to_local(*f.to_world(*local))
|
||||
assert back == pytest.approx(local, abs=1e-6)
|
||||
|
||||
|
||||
# ---- 두 기둥 중심으로 프레임 만들기 (대호지교 P1 값) ----
|
||||
|
||||
def test_from_two_points_origin_is_midpoint():
|
||||
f = fr.from_two_points((480708784.0, 153831202.0), (480721068.0, 153835457.0))
|
||||
assert f.origin_x == pytest.approx(480714926.0)
|
||||
assert f.origin_y == pytest.approx(153833329.5)
|
||||
|
||||
|
||||
def test_from_two_points_angle_matches_p1():
|
||||
f = fr.from_two_points((480708784.0, 153831202.0), (480721068.0, 153835457.0))
|
||||
assert f.angle == pytest.approx(math.atan2(4255.0, 12284.0))
|
||||
assert math.degrees(f.angle) == pytest.approx(19.105, abs=0.001)
|
||||
|
||||
|
||||
def test_p1_columns_are_on_s_axis_at_half_spacing():
|
||||
a = (480708784.0, 153831202.0)
|
||||
b = (480721068.0, 153835457.0)
|
||||
f = fr.from_two_points(a, b)
|
||||
sa, ta, _ = f.to_local(a[0], a[1], 0.0)
|
||||
sb, tb, _ = f.to_local(b[0], b[1], 0.0)
|
||||
# 기둥 중심간격 13,000 (좌표 반올림 때문에 0.1mm 이내)
|
||||
assert sa == pytest.approx(-6500.0, abs=0.1)
|
||||
assert sb == pytest.approx(6500.0, abs=0.1)
|
||||
assert ta == pytest.approx(0.0, abs=1e-6)
|
||||
assert tb == pytest.approx(0.0, abs=1e-6)
|
||||
|
||||
|
||||
def test_from_two_points_rejects_same_point():
|
||||
with pytest.raises(ValueError):
|
||||
fr.from_two_points((1.0, 1.0), (1.0, 1.0))
|
||||
|
||||
|
||||
# ---- 프레임 바꾸기 (언제든 다시 바꿀 수 있어야 함) ----
|
||||
|
||||
def test_convert_between_frames_matches_going_through_world():
|
||||
f1 = fr.Frame(100.0, 200.0, 0.2)
|
||||
f2 = fr.Frame(-500.0, 40.0, 1.1)
|
||||
local1 = (321.0, -45.0, 12.0)
|
||||
via_world = f2.to_local(*f1.to_world(*local1))
|
||||
assert fr.convert(local1, f1, f2) == pytest.approx(via_world, abs=1e-6)
|
||||
|
||||
|
||||
def test_convert_there_and_back_gives_original():
|
||||
f1 = fr.Frame(100.0, 200.0, 0.2)
|
||||
f2 = fr.Frame(-500.0, 40.0, 1.1)
|
||||
local1 = (321.0, -45.0, 12.0)
|
||||
back = fr.convert(fr.convert(local1, f1, f2), f2, f1)
|
||||
assert back == pytest.approx(local1, abs=1e-6)
|
||||
|
||||
|
||||
def test_convert_same_frame_changes_nothing():
|
||||
f = fr.Frame(100.0, 200.0, 0.2)
|
||||
assert fr.convert((1.0, 2.0, 3.0), f, f) == pytest.approx((1.0, 2.0, 3.0))
|
||||
|
||||
|
||||
def test_new_origin_changes_local_values_but_not_world():
|
||||
# 원점만 바꿔도 월드 위치는 그대로, 로컬 값만 달라집니다.
|
||||
world = (1000.0, 2000.0, 5.0)
|
||||
f1 = fr.Frame(0.0, 0.0, 0.3)
|
||||
f2 = fr.Frame(500.0, 500.0, 0.3)
|
||||
l1 = f1.to_local(*world)
|
||||
l2 = f2.to_local(*world)
|
||||
assert l1 != pytest.approx(l2)
|
||||
assert f1.to_world(*l1) == pytest.approx(world, abs=1e-6)
|
||||
assert f2.to_world(*l2) == pytest.approx(world, abs=1e-6)
|
||||
|
||||
|
||||
# ---- 방향 벡터 (이동 없이 회전만) ----
|
||||
|
||||
def test_direction_to_local_ignores_origin():
|
||||
f = fr.Frame(1000.0, 2000.0, math.pi / 2)
|
||||
assert f.direction_to_local(0.0, 1.0, 0.0) == pytest.approx((1.0, 0.0, 0.0))
|
||||
|
||||
|
||||
def test_direction_round_trip():
|
||||
f = fr.Frame(1000.0, 2000.0, 0.7)
|
||||
d = (0.3, -0.8, 0.5)
|
||||
assert f.direction_to_world(*f.direction_to_local(*d)) == pytest.approx(d)
|
||||
|
||||
|
||||
# ---- 점 여러 개 ----
|
||||
|
||||
def test_to_local_many_matches_single_calls():
|
||||
f = fr.Frame(10.0, 20.0, 0.4)
|
||||
pts = [(1.0, 2.0, 3.0), (100.0, -50.0, 7.0)]
|
||||
assert f.to_local_many(pts) == [f.to_local(*p) for p in pts]
|
||||
|
||||
|
||||
def test_to_world_many_matches_single_calls():
|
||||
f = fr.Frame(10.0, 20.0, 0.4)
|
||||
pts = [(1.0, 2.0, 3.0), (100.0, -50.0, 7.0)]
|
||||
assert f.to_world_many(pts) == [f.to_world(*p) for p in pts]
|
||||
Reference in new issue
Block a user