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,123 @@
|
||||
"""단면 외곽선에서 철근 중심선 꺾임점을 계산하는 모듈.
|
||||
|
||||
Rhino 없이 실행됩니다. 단위는 mm, 각도는 도(degree)입니다.
|
||||
2D 좌표는 (s, z): s = 길이방향, z = 높이 (frame.py 의 로컬 좌표와 같은 방향).
|
||||
|
||||
방법 (기존 프로젝트 rebar_pipeline/README.md §3):
|
||||
1. 단면 외곽의 각 면을 안쪽으로 피복만큼 옮긴 직선으로 바꿉니다.
|
||||
2. 이웃한 두 직선의 교점이 철근 중심선의 꺾임점입니다.
|
||||
3. 시작과 끝은 도면의 고정 길이만큼 연장합니다.
|
||||
"""
|
||||
import math
|
||||
|
||||
EPS = 1e-9
|
||||
|
||||
|
||||
def _unit_direction(p0, p1):
|
||||
dx = p1[0] - p0[0]
|
||||
dz = p1[1] - p0[1]
|
||||
length = math.hypot(dx, dz)
|
||||
if length < EPS:
|
||||
raise ValueError("길이가 0인 선분입니다: %r" % (p0,))
|
||||
return (dx / length, dz / length)
|
||||
|
||||
|
||||
def _normal(direction, side):
|
||||
"""진행 방향의 왼쪽 또는 오른쪽 단위 법선."""
|
||||
dx, dz = direction
|
||||
if side == "left":
|
||||
return (-dz, dx)
|
||||
if side == "right":
|
||||
return (dz, -dx)
|
||||
raise ValueError("side 는 'left' 또는 'right' 여야 합니다: %r" % (side,))
|
||||
|
||||
|
||||
def offset_line(p0, p1, distance, side):
|
||||
"""선분 p0->p1 을 진행 방향의 side(left/right)로 distance 만큼 옮긴 직선.
|
||||
|
||||
돌려주는 값: (직선 위의 한 점, 진행 방향 단위벡터)
|
||||
"""
|
||||
direction = _unit_direction(p0, p1)
|
||||
n = _normal(direction, side)
|
||||
point = (p0[0] + n[0] * distance, p0[1] + n[1] * distance)
|
||||
return point, direction
|
||||
|
||||
|
||||
def line_intersection(line_a, line_b):
|
||||
"""두 직선 (점, 방향) 의 교점. 평행하면 ValueError."""
|
||||
(ax, az), (adx, adz) = line_a
|
||||
(bx, bz), (bdx, bdz) = line_b
|
||||
cross = adx * bdz - adz * bdx
|
||||
if abs(cross) < EPS:
|
||||
raise ValueError("두 직선이 평행해서 교점이 없습니다")
|
||||
t = ((bx - ax) * bdz - (bz - az) * bdx) / cross
|
||||
return (ax + adx * t, az + adz * t)
|
||||
|
||||
|
||||
def centerline_points(segments, cover, side, start_len=None, end_len=None):
|
||||
"""철근 중심선의 꺾임점 목록 (s, z).
|
||||
|
||||
segments : 외곽선 선분 목록 [(시작점, 끝점), ...]. 철근이 지나가는 순서와 방향대로 적습니다.
|
||||
cover : 피복 (철근 중심에서 면까지 거리)
|
||||
side : 콘크리트 안쪽이 진행 방향의 'left' 인지 'right' 인지
|
||||
start_len: 첫 꺾임점에서 첫 선분 반대 방향으로 연장할 길이. None 이면 첫 선분의 시작점을 그대로 씁니다.
|
||||
end_len : 마지막 꺾임점에서 마지막 선분 방향으로 연장할 길이. None 이면 마지막 선분의 끝점을 그대로 씁니다.
|
||||
"""
|
||||
if not segments:
|
||||
raise ValueError("선분이 하나 이상 필요합니다")
|
||||
lines = [offset_line(p0, p1, cover, side) for (p0, p1) in segments]
|
||||
|
||||
if len(lines) == 1:
|
||||
if start_len is not None or end_len is not None:
|
||||
raise ValueError("선분이 1개이면 고정 길이를 쓸 수 없습니다")
|
||||
(p0, d) = lines[0]
|
||||
length = math.hypot(segments[0][1][0] - segments[0][0][0],
|
||||
segments[0][1][1] - segments[0][0][1])
|
||||
return [p0, (p0[0] + d[0] * length, p0[1] + d[1] * length)]
|
||||
|
||||
vertices = [line_intersection(lines[i], lines[i + 1]) for i in range(len(lines) - 1)]
|
||||
|
||||
if start_len is None:
|
||||
first = lines[0][0]
|
||||
else:
|
||||
d0 = lines[0][1]
|
||||
first = (vertices[0][0] - d0[0] * start_len, vertices[0][1] - d0[1] * start_len)
|
||||
|
||||
if end_len is None:
|
||||
seg_end = segments[-1][1]
|
||||
n = _normal(lines[-1][1], side)
|
||||
last = (seg_end[0] + n[0] * cover, seg_end[1] + n[1] * cover)
|
||||
else:
|
||||
dn = lines[-1][1]
|
||||
last = (vertices[-1][0] + dn[0] * end_len, vertices[-1][1] + dn[1] * end_len)
|
||||
|
||||
return [first] + vertices + [last]
|
||||
|
||||
|
||||
def segment_lengths(points):
|
||||
"""꺾임점 목록의 각 구간 길이."""
|
||||
return [math.hypot(b[0] - a[0], b[1] - a[1]) for a, b in zip(points, points[1:])]
|
||||
|
||||
|
||||
def polyline_length(points):
|
||||
"""꺾임점을 이은 전체 길이."""
|
||||
return sum(segment_lengths(points))
|
||||
|
||||
|
||||
def turn_angles(points):
|
||||
"""각 안쪽 꺾임점에서 진행 방향이 바뀌는 각도(도, 0~180). 직선이면 0, 직각이면 90."""
|
||||
angles = []
|
||||
for i in range(1, len(points) - 1):
|
||||
d1 = _unit_direction(points[i - 1], points[i])
|
||||
d2 = _unit_direction(points[i], points[i + 1])
|
||||
dot = max(-1.0, min(1.0, d1[0] * d2[0] + d1[1] * d2[1]))
|
||||
angles.append(math.degrees(math.acos(dot)))
|
||||
return angles
|
||||
|
||||
|
||||
def distance_to_line(point, line_p0, line_p1):
|
||||
"""점에서 (선분이 아니라) 무한 직선 line_p0-line_p1 까지의 수직거리."""
|
||||
d = _unit_direction(line_p0, line_p1)
|
||||
vx = point[0] - line_p0[0]
|
||||
vz = point[1] - line_p0[1]
|
||||
return abs(vx * d[1] - vz * d[0])
|
||||
Reference in new issue
Block a user