# -*- coding: utf-8 -*- # 06_flip_hooks.py (대호지교 P1, 2026-10-06 검증) # 목적: 수평전단철근(VIII: 직선 + 양끝 135° 수평 갈고리) 중 폭방향 특정 위치(|t|≈TARGET_T)의 철근만 # 자기 축을 기준으로 거울 반전해서 갈고리 방향을 반대로 뒤집는다 (길이 불변). # 실행: execute_rhino_code, MARKS 한 번에 돌려도 되지만 개수가 EXPECTED와 다르면 수정하지 않고 보고만 한다. # 구조 가정: 마크 idef(C20/C22/C24)의 리프 Brep = 철근 1본, 인스턴스 변환은 보통 단위행렬(리프가 월드 좌표). import Rhino, json, io, os, math import scriptcontext as sc doc = sc.doc OX, OY = 480714926.0, 153833329.5 ANG = math.atan2(4255.0, 12284.0) TARGET_T = 650.0 # 대상 철근의 |t| (centroid 기준, 갈고리 때문에 축에서 ~2mm 벗어남) T_BAND = 40.0 EXPECTED = {"C20": 8, "C22": 10, "C24": 12} # 위치 2곳 x 단 수 x 구역 수 (불일치 시 중단) ca = math.cos(ANG) sa = math.sin(ANG) folder = os.path.dirname(doc.Path) res = {} plan = {} es = Rhino.DocObjects.ObjectEnumeratorSettings() es.HiddenObjects = True # 꺼진 레이어 객체도 열거 (기본값은 제외함!) es.NormalObjects = True es.LockedObjects = True for mk in EXPECTED: inst = None for o in doc.Objects.GetObjectList(es): g = o.Geometry if isinstance(g, Rhino.Geometry.InstanceReferenceGeometry): if doc.Layers[o.Attributes.LayerIndex].FullPath.endswith(mk + "_H16"): inst = g T = inst.Xform ok, inv = T.TryGetInverse() idf = doc.InstanceDefinitions.FindId(inst.ParentIdefId) objs = idf.GetObjects() sel = [] for idx in range(len(objs)): w = objs[idx].Geometry.Duplicate() w.Transform(T) c = Rhino.Geometry.AreaMassProperties.Compute(w).Centroid t = -(c.X - OX) * sa + (c.Y - OY) * ca if abs(abs(t) - TARGET_T) < T_BAND: sel.append(idx) plan[mk] = [T, inv, idf, objs, sel] res[mk] = {"selected": len(sel), "expected": EXPECTED[mk]} good = True for mk in EXPECTED: if res[mk]["selected"] != EXPECTED[mk]: good = False res["all_counts_match"] = good if good: for mk in EXPECTED: T = plan[mk][0] inv = plan[mk][1] idf = plan[mk][2] objs = plan[mk][3] sel = plan[mk][4] bk = mk + "_bk1006c" if doc.InstanceDefinitions.Find(bk) is None: bl = [] al = [] for ob in objs: bl.append(ob.Geometry.Duplicate()) al.append(ob.Attributes.Duplicate()) doc.InstanceDefinitions.Add(bk, "backup before hook flip", Rhino.Geometry.Point3d.Origin, bl, al) newg = [] attrs = [] for idx in range(len(objs)): attrs.append(objs[idx].Attributes.Duplicate()) bg = objs[idx].Geometry if idx not in sel: newg.append(bg.Duplicate()) continue w = bg.Duplicate() w.Transform(T) cen = w.GetBoundingBox(True).Center # 철근 중간 s에서 s-수직 평면으로 자르면 직선부 단면 원 -> 그 중심이 철근 축 위의 점 plane = Rhino.Geometry.Plane(cen, Rhino.Geometry.Vector3d(ca, sa, 0.0)) curves = Rhino.Geometry.Intersect.Intersection.BrepPlane(w, plane, 0.01)[1] axis_pt = Rhino.Geometry.AreaMassProperties.Compute(curves[0]).Centroid nrm = Rhino.Geometry.Vector3d(-sa, ca, 0.0) # 폭방향 t w.Transform(Rhino.Geometry.Transform.Mirror(axis_pt, nrm)) w.Flip() # 거울 반전 뒤 법선 복구 w.Transform(inv) newg.append(w) doc.InstanceDefinitions.ModifyGeometry(idf.Index, newg, attrs) doc.Views.Redraw() f = io.open(os.path.join(folder, "flip_result.json"), "w", encoding="utf-8") txt = json.dumps(res) try: f.write(txt) except TypeError: f.write(txt.decode("utf-8")) f.close()