#! python3 # C-1 apply: replace bend radii < KDS minimum in 4 review models (footing marks B* excluded by user decision). # Geometry is replaced in place (ids, names, user strings, layers kept); results are saved under NEW names. import sys, os, json, math, shutil, collections, traceback sys.path.insert(0, r"C:\Users\User\AppData\Local\PierApp\jobs\c1_work") import c1_core import Rhino from c1_core import BASE, plan_curve, make_pipe, required_rc DATE = "20261023" MODELS = { "ungok": (os.path.join(BASE, "철근 모델랑 운곡교P1_바이브코딩"), "운곡교p1.3dm", "운곡교p1_대조코드표식.3dm"), "sanoe": (os.path.join(BASE, "검토_산외교P1"), "산외교P1 G5.3dm", "산외교P1 G5_대조코드표식.3dm"), "okseong": (os.path.join(BASE, "검토_옥성교P1"), "옥성교P1_G5.3dm", "옥성교P1_G5_대조코드표식.3dm"), "rampa": (os.path.join(BASE, "Ramp-A교P1"), "ramp-A G5.3dm", "ramp-A G5_대조코드표식.3dm"), } OUT = r"C:\Users\User\AppData\Local\PierApp\jobs\c1_work\c1_apply.json" res = {} def is_footing(mark, layer): return ("기초" in layer) or (mark or "").upper().startswith("B") def short_arcs_left(doc, lay, st): """Count short arcs per (footing?) in centreline curves of the document (post check).""" cnt = collections.Counter() for o in doc.Objects.GetObjectList(st): g = o.Geometry L = lay.get(o.Attributes.LayerIndex, "") if isinstance(g, Rhino.Geometry.PolyCurve) and "중심선" in L: dia = o.Attributes.GetUserString("dia") req, d = required_rc(dia) for i in range(g.SegmentCount): s = g.SegmentCurve(i) ok, arc = s.TryGetArc() if ok and not s.IsLinear() and arc.Radius < req - 1e-6 and math.degrees(arc.Angle) < 179.0: cnt["footing" if is_footing(o.Attributes.GetUserString("mark"), L) else "other"] += 1 return dict(cnt) try: for key, (folder, plain, reviewed) in MODELS.items(): r = {"folder": folder} res[key] = r # 1) reviewed model copy (same folder) src = os.path.join(folder, reviewed) dst = os.path.join(folder, reviewed.replace("_대조코드표식", "_검토완료")) if os.path.exists(dst): r["reviewed_copy"] = "exists: " + dst else: shutil.copy2(src, dst) r["reviewed_copy"] = dst # 2) modify plain model headless doc = Rhino.RhinoDoc.OpenHeadless(os.path.join(folder, plain)) 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 r["short_before"] = short_arcs_left(doc, lay, st) breps = collections.defaultdict(list) curves = [] for o in doc.Objects.GetObjectList(st): g = o.Geometry L = lay.get(o.Attributes.LayerIndex, "") if isinstance(g, Rhino.Geometry.Brep) and "철근" in L: breps[o.Attributes.Name].append(o) elif isinstance(g, Rhino.Geometry.Curve) and "중심선" in L: curves.append(o) stat = collections.Counter() per_mark = collections.OrderedDict() for o in curves: a = o.Attributes mark = a.GetUserString("mark") dia = a.GetUserString("dia") L = lay.get(a.LayerIndex, "") c = o.Geometry if not isinstance(c, Rhino.Geometry.PolyCurve): continue pc, info = plan_curve(c, dia) if info["status"] == "unchanged": continue if is_footing(mark, L): stat["held_footing"] += 1 continue if info["status"] != "ok": stat["skipped_complex"] += 1 continue nb = breps.get(a.Name.replace("-RC-", "-R-"), []) if len(nb) != 1: stat["skipped_no_brep"] += 1 continue req, d = required_rc(dia) pipes = make_pipe(pc, d, tol) if not pipes or len(pipes) != 1 or not pipes[0].IsSolid: stat["skipped_pipe_fail"] += 1 continue v_new = Rhino.Geometry.VolumeMassProperties.Compute(pipes[0]).Volume if abs(v_new / (math.pi * (d / 2.0) ** 2 * pc.GetLength()) - 1.0) > 0.002: stat["skipped_volume"] += 1 continue dl = pc.GetLength() - c.GetLength() old_id, brep_id = o.Id, nb[0].Id okc = doc.Objects.Replace(old_id, pc) okb = doc.Objects.Replace(brep_id, pipes[0]) if not (okc and okb): stat["replace_failed"] += 1 continue for oid in (old_id, brep_id): ro = doc.Objects.FindId(oid) at = ro.Attributes.Duplicate() at.SetUserString("bend_r_fix", "C-1 KDS 24 14 21 Table 4.5-1, Rc %.2f mm, %s" % (req, DATE)) doc.Objects.ModifyAttributes(ro, at, True) stat["replaced"] += 1 pm = per_mark.setdefault(mark + "/" + dia, [0, 0.0]) pm[0] += 1 pm[1] += dl r["stat"] = dict(stat) r["per_mark"] = {k: {"bars": v[0], "sum_dlen_mm": round(v[1], 3)} for k, v in per_mark.items()} r["short_after"] = short_arcs_left(doc, lay, st) r["objects_after"] = doc.Objects.Count out_path = os.path.join(folder, os.path.splitext(plain)[0] + "_수정_" + DATE + ".3dm") r["saved"] = bool(doc.WriteFile(out_path, Rhino.FileIO.FileWriteOptions())) r["out"] = out_path doc.Dispose() json.dump(res, open(OUT, "w", encoding="utf-8"), ensure_ascii=False, indent=1) except Exception: res["error"] = traceback.format_exc() res["done"] = True json.dump(res, open(OUT, "w", encoding="utf-8"), ensure_ascii=False, indent=1)