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,264 @@
|
||||
"""G1 gate checker for the "single shaft" form (Ramp-A P1, 2026-10-13): drilled shaft, column, Y coping,
|
||||
pedestals, pads.
|
||||
|
||||
Globals: __rhino_doc__, EXPECTED_PATH, RESULT_PATH. Expected values (section G1 of EXPECTED_PATH) come from
|
||||
drawing dimension texts / EL texts / tables; generator params are not read. Two inputs have no dimension text
|
||||
and are drawn values (marked "[작도]" in the item names): the plan end arc corner and the pedestal rotations.
|
||||
Checks: C5 population, C2 closed solids, C1 extents, C4 coping sections (front Y=0, side X=0, plan end),
|
||||
C3 joints and overlaps, C6 volumes (coping integrated from the dimension outline), C7 pedestals / pads.
|
||||
"""
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
import Rhino.Geometry as rg
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from _common import all_objects, load_expected, write_result # noqa: E402
|
||||
|
||||
doc = __rhino_doc__
|
||||
X = load_expected(EXPECTED_PATH)
|
||||
PID, TOL, G = X["pier_id"], X["tol_mm"], X["G1"]
|
||||
PI, CO, C = G["pile"], G["column"], G["coping"]
|
||||
results = []
|
||||
|
||||
# expected outline from the dimensions
|
||||
ZB = CO["z0"] + CO["length"] # column top = coping bottom
|
||||
ZT = ZB + C["soffit_rise"] # tip level (end face bottom)
|
||||
ZTL, ZTR = C["z_top_left"], C["z_top_right"]
|
||||
HB, HT = C["column_face_half"], C["bottom_length"] / 2
|
||||
TOPH, R = C["top_length"] / 2, C["soffit_radius"]
|
||||
WT, WB = C["width_y"] / 2, C["side_bottom_width"] / 2
|
||||
PCORNER = C["plan_end_corner_x"] # [작도]
|
||||
|
||||
|
||||
def soffit_centre():
|
||||
"""Centre of the R arc through (-HB, ZB) and (-HT, ZT), on the outer (lower-left) side."""
|
||||
ax, az, bx, bz = -HB, ZB, -HT, ZT
|
||||
mx, mz = (ax + bx) / 2, (az + bz) / 2
|
||||
d = math.hypot(bx - ax, bz - az)
|
||||
h = math.sqrt(R * R - d * d / 4)
|
||||
ux, uz = (bz - az) / d, -(bx - ax) / d # unit normal of the chord
|
||||
c1, c2 = (mx + h * ux, mz + h * uz), (mx - h * ux, mz - h * uz)
|
||||
return min(c1, c2, key=lambda c: c[1]) # the centre below the chord
|
||||
|
||||
|
||||
CX, CZ = soffit_centre()
|
||||
|
||||
|
||||
def soffit_x(z):
|
||||
"""|x| of the soffit at height z (ZB..ZT)."""
|
||||
return abs(CX + math.sqrt(R * R - (z - CZ) ** 2))
|
||||
|
||||
|
||||
def plan_arc(y):
|
||||
sag = HT - PCORNER
|
||||
rr = (WT * WT + sag * sag) / (2 * sag)
|
||||
return HT - (rr - math.sqrt(rr * rr - y * y))
|
||||
|
||||
|
||||
def end_x(y, z, zt):
|
||||
"""|x| of the ruled end face at (y, z): plan arc at ZT, straight top edge TOPH at zt."""
|
||||
t = (z - ZT) / (zt - ZT)
|
||||
return plan_arc(y) + t * (TOPH - plan_arc(y))
|
||||
|
||||
|
||||
def objs(prefix):
|
||||
return [o for o in all_objects(doc) if (o.Attributes.GetUserString("id") or "").startswith(prefix)]
|
||||
|
||||
|
||||
def brep(o):
|
||||
return o.Geometry if isinstance(o.Geometry, rg.Brep) else rg.Brep.TryConvertBrep(o.Geometry)
|
||||
|
||||
|
||||
def vol(b):
|
||||
return rg.VolumeMassProperties.Compute(b).Volume
|
||||
|
||||
|
||||
def bbox(b):
|
||||
return b.GetBoundingBox(True)
|
||||
|
||||
|
||||
def check(cid, name, expected, actual, tol=TOL):
|
||||
results.append(dict(check=cid, item=name, expected=round(expected, 3), actual=round(actual, 3),
|
||||
diff=round(actual - expected, 3), tol=tol, result="PASS" if abs(actual - expected) <= tol else "FAIL"))
|
||||
|
||||
|
||||
def flag(cid, name, ok, detail=""):
|
||||
results.append(dict(check=cid, item=name, detail=detail, result="PASS" if ok else "FAIL"))
|
||||
|
||||
|
||||
def coping_volume_expected():
|
||||
"""Sum over z slices and y strips of the x extent: soffit (below ZT) limited by the end face continued
|
||||
below ZT, end face above ZT; left half up to ZTL, right half up to ZTR."""
|
||||
total, nz, ny = 0.0, 3000, 200
|
||||
z1 = max(ZTL, ZTR)
|
||||
dz = (z1 - ZB) / nz
|
||||
for i in range(nz):
|
||||
z = ZB + (i + 0.5) * dz
|
||||
w = WB + (WT - WB) * (z - ZB) / (ZT - ZB) if z < ZT else WT
|
||||
dy = 2 * w / ny
|
||||
area = 0.0
|
||||
for zt in (ZTL, ZTR):
|
||||
if z > zt:
|
||||
continue
|
||||
for j in range(ny):
|
||||
y = -w + (j + 0.5) * dy
|
||||
xe = end_x(y, z, zt)
|
||||
area += (min(soffit_x(z), xe) if z < ZT else xe) * dy
|
||||
total += area * dz
|
||||
return total
|
||||
|
||||
|
||||
def main():
|
||||
pl = sorted(objs(f"{PID}-PL"), key=lambda o: o.Attributes.GetUserString("id"))
|
||||
counts = {k: len(objs(f"{PID}-{k}")) for k in ("LC", "FT", "CL", "CP", "PL")}
|
||||
flag("C5", "object counts", counts == {"LC": 0, "FT": 0, "CL": 1, "CP": 1, "PL": len(PI["segments"])}, str(counts))
|
||||
cl, cp = brep(objs(f"{PID}-CL")[0]), brep(objs(f"{PID}-CP")[0])
|
||||
pls = [brep(o) for o in pl]
|
||||
allb = pls + [cl, cp]
|
||||
flag("C2", f"IsSolid all {len(allb)}", all(b.IsSolid for b in allb), f"{sum(b.IsSolid for b in allb)}/{len(allb)}")
|
||||
|
||||
# C1 pile segments (bottom to top), column, coping extents
|
||||
z = PI["z_bottom"]
|
||||
for k, (s, b) in enumerate(zip(PI["segments"], pls), 1):
|
||||
bb = bbox(b)
|
||||
check("C1", f"pile segment {k} diameter X", s["d"], bb.Max.X - bb.Min.X)
|
||||
check("C1", f"pile segment {k} diameter Y", s["d"], bb.Max.Y - bb.Min.Y)
|
||||
check("C1", f"pile segment {k} Zbot", z, bb.Min.Z)
|
||||
check("C1", f"pile segment {k} Ztop", z + s["length"], bb.Max.Z)
|
||||
z += s["length"]
|
||||
check("C1", "pile top = EL text", CO["z0"], z)
|
||||
bb = bbox(cl)
|
||||
check("C1", "column diameter X", CO["d"], bb.Max.X - bb.Min.X)
|
||||
check("C1", "column diameter Y", CO["d"], bb.Max.Y - bb.Min.Y)
|
||||
check("C1", "column Zbot", CO["z0"], bb.Min.Z)
|
||||
check("C1", "column Ztop", ZB, bb.Max.Z)
|
||||
bb = bbox(cp)
|
||||
check("C1", "coping length X (bottom tips)", 2 * HT, bb.Max.X - bb.Min.X)
|
||||
check("C1", "coping width Y", 2 * WT, bb.Max.Y - bb.Min.Y)
|
||||
check("C1", "coping Zbot", ZB, bb.Min.Z)
|
||||
check("C1", "coping Ztop", max(ZTL, ZTR), bb.Max.Z)
|
||||
check("C1", "coping centre X", 0, (bb.Max.X + bb.Min.X) / 2)
|
||||
check("C1", "total height (pile bottom - coping top left)", G["total_height"], ZTL - PI["z_bottom"])
|
||||
|
||||
# C4 front section at Y = 0
|
||||
def section(plane):
|
||||
ok, curves, pts = rg.Intersect.Intersection.BrepPlane(cp, plane, 0.01)
|
||||
return rg.Curve.JoinCurves(curves, 0.1)
|
||||
|
||||
sec = section(rg.Plane(rg.Point3d(0, 0, 0), rg.Vector3d.YAxis))
|
||||
flag("C4", "front section closed single loop", len(sec) == 1 and sec[0].IsClosed, f"{len(sec)} loop(s)")
|
||||
|
||||
def hits(crv, a, b):
|
||||
return sorted(ev.PointA for ev in rg.Intersect.Intersection.CurveCurve(crv, rg.LineCurve(a, b), 0.01, 0.01))
|
||||
|
||||
def z_at(x, upper):
|
||||
zs = sorted(p.Z for p in hits(sec[0], rg.Point3d(x, 0, 0), rg.Point3d(x, 0, 200000)))
|
||||
return zs[-1] if upper else zs[0]
|
||||
|
||||
for x in (0, HB - 1, -(HB - 1)):
|
||||
check("C4", f"bottom at X={x:+.0f} (column top)", ZB, z_at(x, False))
|
||||
for x in (1500, -1500, 2500, -2500, 3500, -3500, HT - 5, -(HT - 5)):
|
||||
check("C4", f"soffit R{R:.0f} at X={x:+.0f}", CZ + math.sqrt(R * R - (abs(x) + CX) ** 2), z_at(x, False))
|
||||
for x in (-3000, -1000, 1000, 3000):
|
||||
check("C4", f"top at X={x:+.0f}", ZTL if x < 0 else ZTR, z_at(x, True))
|
||||
for x in (-(TOPH + HT) / 2, (TOPH + HT) / 2):
|
||||
zt = ZTL if x < 0 else ZTR
|
||||
check("C4", f"end face at X={x:+.0f} (top {TOPH:.0f} -> bottom {HT:.0f})",
|
||||
ZT + (zt - ZT) * (HT - abs(x)) / (HT - TOPH), z_at(x, True))
|
||||
check("C4", "top step at the pier centre (right - left)", C["top_step"], z_at(1, True) - z_at(-1, True))
|
||||
|
||||
# C4 side section at X = 1 (X = 0 lies on the top step face, 2026-10-13): width 2,000 at the column top ->
|
||||
# 2,200 at the tip level, constant above
|
||||
sec_s = section(rg.Plane(rg.Point3d(1, 0, 0), rg.Vector3d.XAxis))
|
||||
flag("C4", "side section closed single loop", len(sec_s) == 1 and sec_s[0].IsClosed, f"{len(sec_s)} loop(s)")
|
||||
for zz in (ZB + 1, ZB + 750, ZT - 1, ZT + 500):
|
||||
ys = [p.Y for p in hits(sec_s[0], rg.Point3d(1, -5000, zz), rg.Point3d(1, 5000, zz))]
|
||||
w = WB + (WT - WB) * min(zz - ZB, ZT - ZB) / (ZT - ZB)
|
||||
check("C4", f"side width at Z={zz:.0f}", 2 * w, max(ys) - min(ys) if ys else 0)
|
||||
|
||||
# C4 plan end (ruled face: convex arc at the tip level, straight top edge)
|
||||
for sx in (-1, 1):
|
||||
zt = ZTL if sx < 0 else ZTR
|
||||
for zz, y in ((ZT + 5, 0), (ZT + 5, WT - 10), ((ZT + zt) / 2, 0), ((ZT + zt) / 2, WT - 10), (zt - 5, WT - 10)):
|
||||
xe = end_x(y, zz, zt)
|
||||
ins = cp.IsPointInside(rg.Point3d(sx * (xe - 3), y, zz), 0.01, False)
|
||||
out = not cp.IsPointInside(rg.Point3d(sx * (xe + 3), y, zz), 0.01, False)
|
||||
flag("C4", f"end face {'left' if sx < 0 else 'right'} at y={y:.0f} z={zz:.0f} [작도 corner {PCORNER:.0f}]",
|
||||
ins and out, f"x_end {sx * xe:.1f}: inside 3 mm in {ins}, outside 3 mm out {out}")
|
||||
|
||||
# C3 joints and overlaps
|
||||
def inter_vol(a, b):
|
||||
r = rg.Brep.CreateBooleanIntersection(a, b, 0.01)
|
||||
return sum(vol(x) for x in r) if r else 0.0
|
||||
chain = pls + [cl, cp]
|
||||
names = [f"PL{k:02d}" for k in range(1, len(pls) + 1)] + ["CL", "CP"]
|
||||
for (na, a), (nb, b) in zip(zip(names, chain), zip(names[1:], chain[1:])):
|
||||
check("C3", f"{na} top = {nb} bottom", bbox(a).Max.Z, bbox(b).Min.Z, tol=0.01)
|
||||
check("C3", f"overlap {na}-{nb} (mm3)", 0, inter_vol(a, b), tol=1.0)
|
||||
|
||||
# C6 volumes
|
||||
for k, (s, b) in enumerate(zip(PI["segments"], pls), 1):
|
||||
v = math.pi * (s["d"] / 2000) ** 2 * s["length"] / 1000
|
||||
check("C6", f"pile segment {k} volume m3", v, vol(b) / 1e9, tol=v * 0.001)
|
||||
cv_ = math.pi * (CO["d"] / 2000) ** 2 * CO["length"] / 1000
|
||||
check("C6", "column volume m3", cv_, vol(cl) / 1e9, tol=cv_ * 0.001)
|
||||
cpv = coping_volume_expected() / 1e9
|
||||
check("C6", "coping volume m3 (dimension outline)", cpv, vol(cp) / 1e9, tol=cpv * 0.001)
|
||||
|
||||
# C7 pedestals and pads
|
||||
S = G["seats"]
|
||||
nos = sorted(int(k) for k in S["table"])
|
||||
n0 = len(PID) + 3
|
||||
seats = {int(o.Attributes.GetUserString("id")[n0:]): brep(o) for o in objs(f"{PID}-BS")}
|
||||
pads = {int(o.Attributes.GetUserString("id")[n0:]): brep(o) for o in objs(f"{PID}-BP")}
|
||||
flag("C7", "pedestal / pad counts", sorted(seats) == nos and sorted(pads) == nos, f"seats={len(seats)} pads={len(pads)}")
|
||||
allb = list(seats.values()) + list(pads.values())
|
||||
flag("C7", "IsSolid pedestals+pads", all(b.IsSolid for b in allb), f"{sum(b.IsSolid for b in allb)}/{len(allb)}")
|
||||
p_along, p_across = S["pedestal_along"], S["pedestal_across"]
|
||||
m0, m1 = S["pad_margin"]
|
||||
|
||||
def bottom_edges(b):
|
||||
zmin = min(v.Location.Z for v in b.Vertices)
|
||||
return [e.PointAtEnd - e.PointAtStart for e in b.Edges
|
||||
if abs(e.PointAtStart.Z - zmin) < 0.01 and abs(e.PointAtEnd.Z - zmin) < 0.01
|
||||
and e.PointAtStart.DistanceTo(e.PointAtEnd) > 1]
|
||||
|
||||
for no in nos:
|
||||
elx, h1, h2, a, b = S["table"][str(no)]
|
||||
x, y = S["pos"][str(no)]
|
||||
top = ZTL if x < 0 else ZTR
|
||||
s, p = seats[no], pads[no]
|
||||
sb, pb = bbox(s), bbox(p)
|
||||
check("C7", f"NO.{no} table EL.'X' - h1 - h2 = coping top ({'left' if x < 0 else 'right'})", top, elx - h1 - h2)
|
||||
check("C7", f"NO.{no} pedestal bottom = coping top", top, sb.Min.Z)
|
||||
check("C7", f"NO.{no} pedestal top = top + h1", top + h1, sb.Max.Z)
|
||||
check("C7", f"NO.{no} pad top = EL.'X'", elx, pb.Max.Z)
|
||||
ed = bottom_edges(s)
|
||||
al = [v for v in ed if abs(v.Y) > abs(v.X)]
|
||||
ac = [v for v in ed if abs(v.Y) <= abs(v.X)]
|
||||
check("C7", f"NO.{no} pedestal along (C3)", p_along, al[0].Length if al else 0)
|
||||
check("C7", f"NO.{no} pedestal across (C6)", p_across, ac[0].Length if ac else 0)
|
||||
if al:
|
||||
v = al[0] if al[0].Y > 0 else -al[0]
|
||||
check("C7", f"NO.{no} pedestal rotation deg [작도]", S["rotation_deg"][str(no)],
|
||||
math.degrees(math.atan2(v.X, v.Y)), tol=0.05)
|
||||
check("C7", f"NO.{no} pedestal centre X", x, (sb.Max.X + sb.Min.X) / 2)
|
||||
check("C7", f"NO.{no} pedestal centre Y", y, (sb.Max.Y + sb.Min.Y) / 2)
|
||||
c = rg.VolumeMassProperties.Compute(p).Centroid
|
||||
check("C7", f"NO.{no} pad centre X", x, c.X)
|
||||
check("C7", f"NO.{no} pad centre Y", y, c.Y)
|
||||
mm_ = (m0 + m1) / 2
|
||||
pv = h2 / 6 * ((a + m0) * (b + m0) + (a + m1) * (b + m1) + 4 * (a + mm_) * (b + mm_))
|
||||
check("C7", f"NO.{no} pad volume (prismatoid A,B)", pv, vol(p), tol=pv * 0.001)
|
||||
check("C7", f"NO.{no} overlap pedestal-coping (mm3)", 0, inter_vol(s, cp), tol=1.0)
|
||||
check("C7", f"NO.{no} overlap pad-pedestal (mm3)", 0, inter_vol(p, s), tol=1.0)
|
||||
corners = [rg.Point3d(q.X, q.Y, sb.Min.Z - 1) for q in [v.Location for v in s.Vertices] if abs(q.Z - sb.Min.Z) < 0.01]
|
||||
flag("C7", f"NO.{no} pedestal footprint on the coping top", all(cp.IsPointInside(q, 0.01, False) for q in corners),
|
||||
f"{len(corners)} corners tested 1 mm below")
|
||||
write_result(RESULT_PATH, results, dict(volumes_m3={**{f"pile{k}": round(vol(b) / 1e9, 3) for k, b in enumerate(pls, 1)},
|
||||
"column": round(vol(cl) / 1e9, 3), "coping": round(vol(cp) / 1e9, 3)}))
|
||||
|
||||
|
||||
main()
|
||||
Reference in new issue
Block a user