Files
Pier_v1/Code/rebar_pipeline/rhino/05_realign_base_pieces.py
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 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>
2026-10-28 11:20:18 +09:00

136 lines
5.4 KiB
Python

# -*- coding: utf-8 -*-
# 05_realign_base_pieces.py (대호지교 P1, 2026-10-06 검증)
# 목적: 수직전단철근 박스(C12/C14/C16/C18 등)에서 바닥(⊔) 조각이 뚜껑(⊓) 평면에서 s 방향으로
# 어긋난 것을 되돌린다. 피복(헌치 경사면에 수직인 거리)은 유지하려고 경사 접선 방향으로 움직이고,
# 바닥 상단을 뚜껑 윗변 중심에서 다시 자른다(겹침 510 유지).
# 실행: rhino_mcp execute_rhino_code (IronPython 2.7). MARK 하나씩 실행 (소켓 타임아웃 방지).
# 주의: lambda/리스트컴프리헨션 금지(exec 스코프), f-string 금지. 결과는 JSON 파일로 쓰고 따로 읽는다.
# 구조 가정: 마크 idef -> 스테이션 idef -> 박스 idef(리프 Brep: 뚜껑, 바닥, [연장 조각...])
import Rhino, json, io, os, math
import scriptcontext as sc
doc = sc.doc
MARK = "C12" # C12 / C14 / C16 / C18 ...
OX, OY = 480714926.0, 153833329.5 # 코핑 로컬 원점 (코핑 bbox 중심)
ANG = math.atan2(4255.0, 12284.0) # 코핑 축 각도 (두 기둥 중심 연결선)
# 헌치 경사 dz/ds (그 스테이션에서 콘크리트 밑면의 기울기): 끝단 헌치 ±0.375, 안쪽 헌치 ±1/3
SLOPES = {"C12": -0.375, "C18": 0.375, "C14": 1.0 / 3.0, "C16": -1.0 / 3.0}
RBAR = 11.1 # 철근 반지름 (H22)
TOL = 0.5 # 이보다 작은 어긋남은 건너뜀
ca = math.cos(ANG)
sa = math.sin(ANG)
k = SLOPES[MARK]
folder = os.path.dirname(doc.Path)
out = os.path.join(folder, "fix_result_" + MARK + ".json")
inst = None
for o in doc.Objects:
g = o.Geometry
if isinstance(g, Rhino.Geometry.InstanceReferenceGeometry):
if doc.Layers[o.Attributes.LayerIndex].FullPath.endswith(MARK + "_H22"):
inst = g
mxf = inst.Xform
midf = doc.InstanceDefinitions.FindId(inst.ParentIdefId)
# 박스 idef 이름 -> 대표 인스턴스 월드 변환
boxT = {}
for ob in midf.GetObjects():
og = ob.Geometry
if not isinstance(og, Rhino.Geometry.InstanceReferenceGeometry):
continue
sidf = doc.InstanceDefinitions.FindId(og.ParentIdefId)
sxf = mxf * og.Xform
for b in sidf.GetObjects():
bg = b.Geometry
if isinstance(bg, Rhino.Geometry.InstanceReferenceGeometry):
bn = doc.InstanceDefinitions.FindId(bg.ParentIdefId).Name
if bn not in boxT:
boxT[bn] = sxf * bg.Xform
report = []
names = list(boxT.keys())
names.sort()
for bn in names:
T = boxT[bn]
ok, inv = T.TryGetInverse()
bidf = doc.InstanceDefinitions.Find(bn)
objs = bidf.GetObjects()
geoms = []
attrs = []
info = [] # [s, area, bbox minZ, bbox maxZ] (월드)
for ob in objs:
bg = ob.Geometry
amp = Rhino.Geometry.AreaMassProperties.Compute(bg)
c = amp.Centroid
c.Transform(T)
s = (c.X - OX) * ca + (c.Y - OY) * sa
w = bg.Duplicate()
w.Transform(T)
bbox = w.GetBoundingBox(True)
info.append([s, bg.GetArea(), bbox.Min.Z, bbox.Max.Z])
geoms.append(bg.Duplicate())
attrs.append(ob.Attributes.Duplicate()) # ColorFromParent 보존
# 면적 큰 2개 = 뚜껑/바닥 (연장 조각은 작다). 뚜껑 = bbox 최저 z가 더 높은 쪽
i1 = -1
i2 = -1
for i in range(len(info)):
if i1 < 0 or info[i][1] > info[i1][1]:
i2 = i1
i1 = i
elif i2 < 0 or info[i][1] > info[i2][1]:
i2 = i
if info[i1][2] > info[i2][2]:
lid = i1
base = i2
else:
lid = i2
base = i1
dev = info[base][0] - info[lid][0]
row = {"box": bn, "n": len(info), "dev_before": round(dev, 3)}
if abs(dev) <= TOL:
row["action"] = "skip"
report.append(row)
continue
bkname = bn + "_bk1006b" # 백업 idef
if doc.InstanceDefinitions.Find(bkname) is None:
bl = []
al = []
for ob in objs:
bl.append(ob.Geometry.Duplicate())
al.append(ob.Attributes.Duplicate())
doc.InstanceDefinitions.Add(bkname, "backup before s-realign", Rhino.Geometry.Point3d.Origin, bl, al)
ds = -dev
dz = k * ds # 경사 접선 방향 이동 -> 수직 피복 유지
v = Rhino.Geometry.Vector3d(ca * ds, sa * ds, dz)
v.Transform(inv) # 월드 이동벡터 -> idef 로컬
ztop = info[lid][3] - RBAR # 뚜껑 윗변 중심 z (월드)
pl = Rhino.Geometry.Plane(Rhino.Geometry.Point3d(0.0, 0.0, ztop), Rhino.Geometry.Vector3d.ZAxis)
pl.Transform(inv)
newgeoms = []
for i in range(len(geoms)):
if abs(info[i][0] - info[lid][0]) > TOL: # 뚜껑 평면에서 벗어난 조각만 이동
m = geoms[i]
m.Translate(v)
chk = m.Duplicate()
chk.Transform(T)
if chk.GetBoundingBox(True).Max.Z > ztop + 0.05:
tr = m.Trim(pl, 0.01) # +Z 쪽 제거
if tr is not None and len(tr) > 0:
m = tr[0]
newgeoms.append(m)
else:
newgeoms.append(geoms[i])
doc.InstanceDefinitions.ModifyGeometry(bidf.Index, newgeoms, attrs) # 3-인자: 속성 보존
row["action"] = "fixed"
row["dz"] = round(dz, 3)
report.append(row)
doc.Views.Redraw()
txt = json.dumps(report)
f = io.open(out, "w", encoding="utf-8")
try:
f.write(txt)
except TypeError:
f.write(txt.decode("utf-8"))
f.close()