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>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,205 @@
|
||||
"""G1 stage, form "single shaft" (Ramp-A P1, 2026-10-13): drilled shaft in two diameters (no footing, no lean
|
||||
concrete), circular column, Y-shaped coping (R soffit arcs flaring from the column, battered ends that are convex
|
||||
in plan, a step in the top surface), one pedestal (box) and one h2 pad (frustum) per bearing.
|
||||
|
||||
Run inside Rhino 8 via runner with globals PARAMS_PATH. Units mm, Z = EL x 1000, origin = pier centre.
|
||||
params:
|
||||
pile {segments [{d, z0, z1}, ...] bottom to top}; column {shape "circular", diameter, z0, z1}
|
||||
coping {form "y_shaft", z_bottom (= column top), z_tip, half_bottom (column face), soffit {centre_x, centre_z,
|
||||
radius} (left arc; the right one is mirrored), z_top_left, z_top_right, top_half (straight top edge),
|
||||
plan_end {corner_x, tip_x, half_width} (bottom edge arc through (corner_x, +-half_width) and (tip_x, 0)),
|
||||
side {half_width_top, half_width_bottom} (2,200 above z_tip, straight taper to 2,000 at z_bottom)}
|
||||
bearing_seats {pedestal {size_x, size_y}, pad_margin_bottom, pad_margin_top,
|
||||
bearings [{no, centre [x, y], rotation_deg, seat_z, h1, h2, A, B, type}]}
|
||||
Coping = (front outline extruded along Y) n (side outline extruded along X) - two end cutters. The end face is
|
||||
ruled: straight top edge x = +-top_half at the top level, convex plan arc at z_tip. Each extrusion reaches past
|
||||
the faces the other one defines (no coincident or tangent faces in the booleans, see concrete_tapered.py).
|
||||
Not modelled (user decision 2026-10-13): the 30 mm decorative grooves (D1 detail "B").
|
||||
"""
|
||||
import json
|
||||
import math
|
||||
|
||||
import Rhino.Geometry as rg
|
||||
import System
|
||||
from Rhino.DocObjects import ObjectAttributes
|
||||
from System.Drawing import Color
|
||||
|
||||
P = json.load(open(PARAMS_PATH, encoding="utf-8"))
|
||||
doc = __rhino_doc__
|
||||
TOL = 0.01
|
||||
PID = P["pier_id"]
|
||||
FCK = P["materials"]["fck"]
|
||||
LAYERS = {k: (f"{PID}::{name}", Color.FromArgb(*rgb)) for k, (name, rgb) in P["display"]["concrete_layers"].items()}
|
||||
|
||||
|
||||
def layer_index(full_path, color):
|
||||
idx = doc.Layers.FindByFullPath(full_path, -1)
|
||||
if idx >= 0:
|
||||
return idx
|
||||
parent_name, child = full_path.split("::")
|
||||
parent = doc.Layers.FindByFullPath(parent_name, -1)
|
||||
if parent < 0:
|
||||
parent = doc.Layers.Add(parent_name, Color.Black)
|
||||
layer = __import__("Rhino").DocObjects.Layer()
|
||||
layer.Name, layer.Color = child, color
|
||||
layer.ParentLayerId = doc.Layers[parent].Id
|
||||
return doc.Layers.Add(layer)
|
||||
|
||||
|
||||
def add(brep, key, oid, role, fck, views):
|
||||
if brep is None or not brep.IsSolid:
|
||||
raise RuntimeError(f"{oid}: not a closed solid")
|
||||
if not brep.IsValid: # AddBrep silently refuses invalid breps
|
||||
ok, log = brep.IsValidWithLog()
|
||||
raise RuntimeError(f"{oid}: invalid brep: {log[:400]}")
|
||||
attr = ObjectAttributes()
|
||||
attr.LayerIndex = layer_index(*LAYERS[key])
|
||||
attr.Name = oid
|
||||
for k, v in (("id", oid), ("role", role), ("fck", fck), ("source_views", views)):
|
||||
attr.SetUserString(k, v)
|
||||
gid = doc.Objects.AddBrep(brep, attr)
|
||||
if gid == System.Guid.Empty:
|
||||
raise RuntimeError(f"{oid}: AddBrep failed")
|
||||
return gid
|
||||
|
||||
|
||||
def solid(b):
|
||||
if b is not None and b.SolidOrientation == rg.BrepSolidOrientation.Inward:
|
||||
b.Flip()
|
||||
return b
|
||||
|
||||
|
||||
def cylinder(z0, z1, r):
|
||||
return rg.Cylinder(rg.Circle(rg.Plane(rg.Point3d(0, 0, z0), rg.Vector3d.ZAxis), r), z1 - z0).ToBrep(True, True)
|
||||
|
||||
|
||||
def extrude_closed(curve, vec):
|
||||
return solid(rg.Surface.CreateExtrusion(curve, vec).ToBrep().CapPlanarHoles(TOL))
|
||||
|
||||
|
||||
def rotated_rect(cx, cy, along, across, rot_deg, z):
|
||||
"""Rectangle with `along` side on +Y rotated clockwise by rot_deg (plan view)."""
|
||||
t = math.radians(rot_deg)
|
||||
ua, uc = (math.sin(t), math.cos(t)), (math.cos(t), -math.sin(t))
|
||||
pts = [(cx + sa * ua[0] * along / 2 + sc * uc[0] * across / 2, cy + sa * ua[1] * along / 2 + sc * uc[1] * across / 2)
|
||||
for sa, sc in ((-1, -1), (1, -1), (1, 1), (-1, 1))]
|
||||
return rg.PolylineCurve([rg.Point3d(x, y, z) for x, y in pts + pts[:1]])
|
||||
|
||||
|
||||
def frustum(bottom, top):
|
||||
lofts = rg.Brep.CreateFromLoft([bottom, top], rg.Point3d.Unset, rg.Point3d.Unset, rg.LoftType.Straight, False)
|
||||
return solid(lofts[0].CapPlanarHoles(TOL))
|
||||
|
||||
|
||||
def boolean(kind, a, b):
|
||||
f = rg.Brep.CreateBooleanIntersection if kind == "and" else rg.Brep.CreateBooleanDifference
|
||||
out = f(a, b, TOL)
|
||||
if not out or len(out) != 1:
|
||||
raise RuntimeError(f"coping boolean {kind} failed ({len(out) if out else 0} pieces)")
|
||||
return solid(out[0])
|
||||
|
||||
|
||||
def front_extrusion(c):
|
||||
"""Front outline (XZ): soffit arcs from the column face, continued past the end (cut later), vertical outer
|
||||
ends at +-ext, top levels with the step at x = 0. Extruded over y = +-(half_width_top + 100)."""
|
||||
s, zb = c["soffit"], c["z_bottom"]
|
||||
cx, cz, r = s["centre_x"], s["centre_z"], s["radius"]
|
||||
a0 = math.atan2(zb - cz, -c["half_bottom"] - cx) # left arc start (column face)
|
||||
ext = c["plan_end"]["tip_x"] + 400 # past the tip (abs)
|
||||
a1 = math.acos((-ext - cx) / r) if abs((-ext - cx) / r) <= 1 else None
|
||||
if a1 is None:
|
||||
raise RuntimeError("soffit arc does not reach the extension")
|
||||
am = (a0 + a1) / 2 # left arc through start, mid, end
|
||||
pl = [(cx + r * math.cos(a), cz + r * math.sin(a)) for a in (a0, am, a1)] # (x, z)
|
||||
w = c["side"]["half_width_top"] + 100
|
||||
to_xyz = lambda x, z: rg.Point3d(x, -w, z)
|
||||
arc_l = rg.ArcCurve(rg.Arc(*[to_xyz(x, z) for x, z in pl]))
|
||||
arc_r = rg.ArcCurve(rg.Arc(*[to_xyz(-x, z) for x, z in reversed(pl)]))
|
||||
zl, zr = c["z_top_left"], c["z_top_right"]
|
||||
xe, ze = pl[2] # left extension end (xe < 0)
|
||||
poly_top = rg.PolylineCurve([to_xyz(xe, ze), to_xyz(xe, zl), to_xyz(0, zl), to_xyz(0, zr), to_xyz(-xe, zr),
|
||||
to_xyz(-xe, ze)])
|
||||
bottom = rg.LineCurve(to_xyz(c["half_bottom"], zb), to_xyz(-c["half_bottom"], zb))
|
||||
j = rg.Curve.JoinCurves([arc_l, poly_top, arc_r, bottom], 0.01)
|
||||
if len(j) != 1 or not j[0].IsClosed:
|
||||
raise RuntimeError(f"front outline not closed ({len(j)} pieces)")
|
||||
return extrude_closed(j[0], rg.Vector3d(0, 2 * w, 0))
|
||||
|
||||
|
||||
def side_extrusion(c):
|
||||
"""Side outline (YZ): straight taper from the column width at z_bottom to the full width at z_tip, continued
|
||||
100 below z_bottom (no face on the coping bottom plane), vertical above, up past the top. Extruded over X."""
|
||||
sd, zb, zt = c["side"], c["z_bottom"], c["z_tip"]
|
||||
hb, ht = sd["half_width_bottom"], sd["half_width_top"]
|
||||
k = (ht - hb) / (zt - zb)
|
||||
y_low, z_low = hb - k * 100, zb - 100
|
||||
ztop = max(c["z_top_left"], c["z_top_right"]) + 200
|
||||
x0 = c["plan_end"]["tip_x"] + 600
|
||||
pts = [(-y_low, z_low), (y_low, z_low), (ht, zt), (ht, ztop), (-ht, ztop), (-ht, zt)]
|
||||
crv = rg.PolylineCurve([rg.Point3d(-x0, y, z) for y, z in pts + pts[:1]])
|
||||
return extrude_closed(crv, rg.Vector3d(2 * x0, 0, 0))
|
||||
|
||||
|
||||
def end_cutter(c, sx):
|
||||
"""Waste beyond the ruled end face of one end (sx = -1 left, +1 right). Two straight-lofted sections, below the
|
||||
tip level and above the top: on each, x(y) = arc(y) + t * (top_edge - arc(y)), t = (z - z_tip) / (z_top - z_tip),
|
||||
so the loft lines are the rulings of the end face. Plan arc sampled every 25 mm (chord error < 0.1 mm)."""
|
||||
pe, zt = c["plan_end"], c["z_tip"]
|
||||
ztop = c["z_top_left"] if sx < 0 else c["z_top_right"]
|
||||
hw, corner, tip, top = pe["half_width"], pe["corner_x"], pe["tip_x"], c["top_half"]
|
||||
sag = tip - corner
|
||||
R = (hw * hw + sag * sag) / (2 * sag)
|
||||
ys = [-hw - 200 + 25 * i for i in range(int((2 * hw + 400) / 25) + 1)]
|
||||
arc = lambda y: tip - (R - math.sqrt(R * R - y * y)) # abs x of the bottom edge
|
||||
secs = []
|
||||
for z in (zt - 700, ztop + 150):
|
||||
t = (z - zt) / (ztop - zt)
|
||||
pts = [(arc(y) + t * (top - arc(y)), y) for y in ys]
|
||||
pts += [(tip + 1500, ys[-1]), (tip + 1500, ys[0])]
|
||||
secs.append(rg.PolylineCurve([rg.Point3d(sx * x, y, z) for x, y in pts + pts[:1]]))
|
||||
lofts = rg.Brep.CreateFromLoft(secs, rg.Point3d.Unset, rg.Point3d.Unset, rg.LoftType.Straight, False)
|
||||
b = solid(lofts[0].CapPlanarHoles(TOL))
|
||||
if b is None or not b.IsSolid:
|
||||
raise RuntimeError("end cutter is not a closed solid")
|
||||
return b
|
||||
|
||||
|
||||
def coping_brep(c):
|
||||
body = boolean("and", [front_extrusion(c)], [side_extrusion(c)])
|
||||
return boolean("diff", [body], [end_cutter(c, -1), end_cutter(c, 1)])
|
||||
|
||||
|
||||
def main():
|
||||
for o in list(doc.Objects):
|
||||
if o.Attributes.GetUserString("id"):
|
||||
doc.Objects.Delete(o, True)
|
||||
pl, cl, cp, bs = (P[k] for k in ("pile", "column", "coping", "bearing_seats"))
|
||||
segs = pl["segments"]
|
||||
for a, b in zip(segs, segs[1:]):
|
||||
if abs(a["z1"] - b["z0"]) > TOL:
|
||||
raise RuntimeError("pile segments not continuous")
|
||||
if abs(segs[-1]["z1"] - cl["z0"]) > TOL or abs(cl["z1"] - cp["z_bottom"]) > TOL:
|
||||
raise RuntimeError("pile top / column / coping bottom levels do not meet")
|
||||
for i, s in enumerate(segs, 1):
|
||||
add(cylinder(s["z0"], s["z1"], s["d"] / 2), "pile", f"{PID}-PL{i:02d}",
|
||||
f"drilled shaft segment {i} D{s['d']:.0f}", FCK["pile"], "일반도 정면도·측면도")
|
||||
add(cylinder(cl["z0"], cl["z1"], cl["diameter"] / 2), "column", f"{PID}-CL", "column", FCK["column"],
|
||||
"일반도 정면도·측면도·기둥 단면도")
|
||||
add(coping_brep(cp), "coping", f"{PID}-CP", "coping (Y-shaped, single shaft)", FCK["coping"],
|
||||
"일반도 정면도·측면도·평면도")
|
||||
ped, mb, mt = bs["pedestal"], bs["pad_margin_bottom"], bs["pad_margin_top"]
|
||||
for b in bs["bearings"]:
|
||||
cx, cy = b["centre"]
|
||||
z0, z1 = b["seat_z"], b["seat_z"] + b["h1"]
|
||||
base = rotated_rect(cx, cy, ped["size_y"], ped["size_x"], b["rotation_deg"], z0)
|
||||
add(extrude_closed(base, rg.Vector3d(0, 0, b["h1"])), "seat", f"{PID}-BS{b['no']:02d}",
|
||||
f"bearing pedestal h1 NO.{b['no']}", FCK["seat"], "일반도 평면도·교량받침 상세도·ELEVATION TABLE")
|
||||
bottom = rotated_rect(cx, cy, b["A"] + mb, b["B"] + mb, b["rotation_deg"], z1)
|
||||
top = rotated_rect(cx, cy, b["A"] + mt, b["B"] + mt, b["rotation_deg"], z1 + b["h2"])
|
||||
add(frustum(bottom, top), "pad", f"{PID}-BP{b['no']:02d}", f"bearing pad h2 NO.{b['no']} {b['type']}", "-",
|
||||
"일반도 평면도·교량받침 상세도·ELEVATION TABLE")
|
||||
doc.Views.Redraw()
|
||||
print(json.dumps({"objects": len(segs) + 2 + 2 * len(bs["bearings"])}))
|
||||
|
||||
|
||||
main()
|
||||
Reference in new issue
Block a user