- 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>
136 lines
5.4 KiB
Python
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()
|