#! python3 # D: rebuild the end hook of Okseong P5 and Ramp-A P3 / P3-1 from the drawing detail rows (r, c = arc length, d = straight tail). # drawing: P5 r60 c141 d80 | P3 r60 c141 d100 | P3-1 r135 c165 d150 ; L = a+b+c+d. Hook angle = c / r. # Sources: Okseong ..._수정4 -> ..._수정5_20261026 ; Ramp-A ..._수정2 -> ..._수정3_20261026 import sys, os, json, math, collections, traceback sys.path.insert(0, r"C:\Users\User\AppData\Local\PierApp\jobs\c1_work") import c1_core import Rhino from Rhino.Geometry import Arc, ArcCurve, LineCurve, PolyCurve, Point3d, Vector3d, Transform, Brep, PipeCapMode from c1_core import BASE, required_rc DATE_TXT = "2026-10-26" OUT = r"C:\Users\User\AppData\Local\PierApp\jobs\c1_work\c1_hook_cd.json" JOBS = { "okseong": {"src": os.path.join(BASE, "검토_옥성교P1", "옥성교P1_G5_수정4_20261026.3dm"), "dst": os.path.join(BASE, "검토_옥성교P1", "옥성교P1_G5_수정5_20261026.3dm"), "marks": {"P5": (60.0, 141.0, 80.0)}, "note": "hook c/d from drawing: P5 r60 c141 d80"}, "rampa": {"src": os.path.join(BASE, "Ramp-A교P1", "ramp-A G5_수정2_20261026.3dm"), "dst": os.path.join(BASE, "Ramp-A교P1", "ramp-A G5_수정3_20261026.3dm"), "marks": {"P3": (60.0, 141.0, 100.0), "P3-1": (135.0, 165.0, 150.0)}, "note": "hook c/d from drawing: P3 r60 c141 d100, P3-1 r135 c165 d150"}, } res = {} def robust_pipe(curve, d, tol): for fit, atol in ((True, tol), (False, tol), (True, 0.01), (True, 0.1)): rr = Brep.CreatePipe(curve, d / 2.0, False, PipeCapMode.Flat, fit, atol, Rhino.RhinoMath.ToRadians(1.0)) if rr and len(rr) == 1 and rr[0].IsSolid: vol = Rhino.Geometry.VolumeMassProperties.Compute(rr[0]).Volume if abs(vol / (math.pi * (d / 2.0) ** 2 * curve.GetLength()) - 1.0) <= 0.005: return rr[0] return None def rebuild_hook(c, r, carc, dtail): n = c.SegmentCount if n < 3: return None, "few segments" segs = [c.SegmentCurve(i).DuplicateCurve() for i in range(n)] hook, tail = segs[-2], segs[-1] ok, arc = hook.TryGetArc() if not ok or hook.IsLinear() or abs(math.degrees(arc.Angle) - 135.0) > 0.6 or not tail.IsLinear(): return None, "no 135 hook at the end" S = hook.PointAtStart T0 = Vector3d(hook.TangentAtStart) T0.Unitize() u = arc.Center - S u.Unitize() axis = arc.Plane.ZAxis theta = carc / r center = S + u * r rot = Transform.Rotation(theta, axis, center) E = Point3d(S) E.Transform(rot) new_arc = Arc(S, T0, E) if not new_arc.IsValid: return None, "arc invalid" ac = ArcCurve(new_arc) T1 = Vector3d(ac.TangentAtEnd) T1.Unitize() P2 = E + T1 * dtail pc = PolyCurve() for s in segs[:-2]: pc.Append(s) pc.Append(ac) pc.Append(LineCurve(E, P2)) return pc, "ok" def run_one(key, job): r = {} doc = Rhino.RhinoDoc.OpenHeadless(job["src"]) tol = doc.ModelAbsoluteTolerance st = Rhino.DocObjects.ObjectEnumeratorSettings() st.HiddenObjects = True; st.LockedObjects = True; st.NormalObjects = True lay = {l.Index: l.FullPath for l in doc.Layers} r["objects_before"] = doc.Objects.Count breps = collections.defaultdict(list) curves = [] for o in doc.Objects.GetObjectList(st): g = o.Geometry L = lay.get(o.Attributes.LayerIndex, "") if isinstance(g, Brep) and "철근" in L: breps[o.Attributes.Name].append(o) elif isinstance(g, Rhino.Geometry.Curve) and "중심선" in L and o.Attributes.GetUserString("mark") in job["marks"]: curves.append(o) stat = collections.Counter() samples = {} for o in curves: a = o.Attributes m = a.GetUserString("mark") rr_, cc_, dd_ = job["marks"][m] req, d = required_rc(a.GetUserString("dia")) nb = breps.get(a.Name.replace("-RC-", "-R-"), []) c = o.Geometry if len(nb) != 1 or not isinstance(c, PolyCurve): stat[m + "_unexpected"] += 1 continue pc, why = rebuild_hook(c, rr_, cc_, dd_) if pc is None: stat[m + "_" + why] += 1 continue pipe = robust_pipe(pc, d, tol) if pipe is None: stat[m + "_pipe_fail"] += 1 continue cid, bid = o.Id, nb[0].Id if not (doc.Objects.Replace(cid, pc) and doc.Objects.Replace(bid, pipe)): stat[m + "_replace_failed"] += 1 continue for oid in (cid, bid): ro = doc.Objects.FindId(oid) at = ro.Attributes.Duplicate() at.SetUserString("hook_fix", "hook from drawing: r%.0f c%.0f (arc %.1f deg) d%.0f (%s)" % (rr_, cc_, math.degrees(cc_ / rr_), dd_, DATE_TXT)) doc.Objects.ModifyAttributes(ro, at, True) if m not in samples: ok2, arcn = pc.SegmentCurve(pc.SegmentCount - 2).TryGetArc() samples[m] = {"old_len": round(c.GetLength(), 1), "new_len": round(pc.GetLength(), 1), "arc_R": round(arcn.Radius, 2), "arc_deg": round(math.degrees(arcn.Angle), 2), "arc_len": round(arcn.Radius * arcn.Angle, 1), "tail": round(pc.SegmentCurve(pc.SegmentCount - 1).GetLength(), 1)} stat[m + "_done"] += 1 r["stat"] = dict(stat) r["sample"] = samples doc.Strings.SetString("수정일", DATE_TXT) prev = doc.Strings.GetValue("수정내용") or "" doc.Strings.SetString("수정내용", (prev + " | " if prev else "") + job["note"]) r["objects_after"] = doc.Objects.Count r["saved"] = bool(doc.WriteFile(job["dst"], Rhino.FileIO.FileWriteOptions())) r["out"] = job["dst"] doc.Dispose() return r for key, job in JOBS.items(): try: res[key] = run_one(key, job) except Exception: res[key] = {"error": traceback.format_exc()} json.dump(res, open(OUT, "w", encoding="utf-8"), ensure_ascii=False, indent=1) res["done"] = True json.dump(res, open(OUT, "w", encoding="utf-8"), ensure_ascii=False, indent=1)